.NET Core 如何调用泛型IAppRepository<T>所有实现执行批量删除操作
解决方案
完全可以实现批量调用所有已注册仓储的需求,不需要逐个按事件类型调用,以下提供两种可落地的实现方案:
方案1:新增非泛型基接口(最推荐,性能最优)
通过引入非泛型的基接口解决泛型类型不能直接统一遍历的问题,改动量极小:
- 定义非泛型基接口
public interface IAppRepository { Task<IReadOnlyList<IAppEvent>> GetAllEventsByDateTimeAsync(DateTime eventsBeforeDate); Task DeleteEventAsync(string internalRequestId); }
- 调整原有泛型接口继承非泛型基接口
public interface IAppRepository<T> : IAppRepository where T: IAppEvent { // 保留原有泛型方法 new Task<IReadOnlyList<T>> GetAllEventsByDateTimeAsync(DateTime eventsBeforeDate); }
- 在仓储实现类中显式实现非泛型接口方法(以SampleRepo为例)
public class SampleRepo : IAppRepository<SampleEvent> { // 原有泛型方法逻辑不变 public Task<IReadOnlyList<SampleEvent>> GetAllEventsByDateTimeAsync(DateTime eventsBeforeDate) { // 原有实现 } // 新增非泛型方法的显式实现 async Task<IReadOnlyList<IAppEvent>> IAppRepository.GetAllEventsByDateTimeAsync(DateTime eventsBeforeDate) { var result = await GetAllEventsByDateTimeAsync(eventsBeforeDate); return result.ToList(); } // Delete方法参数和非泛型定义完全一致,不需要额外修改 public Task DeleteEventAsync(string internalRequestId) { // 原有实现 } }
- 实现批量获取仓储的方法
public IEnumerable<IAppRepository> GetAllRepos(IServiceProvider serviceProvider) { // 扫描所有实现IAppEvent的类型 var eventTypes = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(ass => ass.GetTypes()) .Where(t => typeof(IAppEvent).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract); foreach (var eventType in eventTypes) { var genericRepoType = typeof(IAppRepository<>).MakeGenericType(eventType); var repoInstance = serviceProvider.GetService(genericRepoType); if (repoInstance != null) { yield return (IAppRepository)repoInstance; } } }
- 最终调用逻辑(注意修正原有异步等待和变量名问题)
var cutoffTime = DateTime.Now; foreach (var repo in GetAllRepos(serviceProvider)) { var oldEvents = await repo.GetAllEventsByDateTimeAsync(cutoffTime); foreach (var appEvent in oldEvents) { await repo.DeleteEventAsync(appEvent.InternalRequestId); } }
方案2:纯反射实现(无需修改原有代码)
如果不想改动现有接口和实现定义,可以直接用反射调用对应方法,零侵入现有逻辑:
public async Task DeleteAllOldEvents(IServiceProvider serviceProvider, DateTime cutoffTime) { // 扫描所有事件类型 var eventTypes = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(ass => ass.GetTypes()) .Where(t => typeof(IAppEvent).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract); foreach (var eventType in eventTypes) { // 解析对应泛型仓储 var genericRepoType = typeof(IAppRepository<>).MakeGenericType(eventType); var repo = serviceProvider.GetService(genericRepoType); if (repo == null) continue; // 反射调用查询方法 var getMethod = genericRepoType.GetMethod(nameof(IAppRepository<IAppEvent>.GetAllEventsByDateTimeAsync)); var getTask = (Task)getMethod.Invoke(repo, new object[] { cutoffTime }); await getTask; // 读取查询结果 var result = (IEnumerable<IAppEvent>)getTask.GetType().GetProperty("Result").GetValue(getTask); // 反射调用删除方法 var deleteMethod = genericRepoType.GetMethod(nameof(IAppRepository<IAppEvent>.DeleteEventAsync)); foreach (var appEvent in result) { var deleteTask = (Task)deleteMethod.Invoke(repo, new object[] { appEvent.InternalRequestId }); await deleteTask; } } }
注意事项
- 原示例代码中
event是C#关键字,不能作为变量名使用,建议替换为appEvent等自定义名称 - 所有异步方法必须添加
await关键字,否则会出现任务未执行完成就进入后续逻辑的问题 - 如果事件类型定义在多个程序集中,需要调整反射扫描的程序集范围避免漏扫
内容的提问来源于stack exchange,提问作者Roni
相关产品推荐
相关产品推荐

