Autofac结合NServiceBus调度器的DbContext释放与依赖注入优化问题
重构方案:自动扫描IJob实现 + 按类型解析实例
我来帮你搞定这两个重构点,让你的代码更灵活易维护,先回顾下你的问题背景:你最初在Start方法里创建了一个外层生命周期范围,解析所有IJob后调度执行,但因为调度委托是延迟触发的,当委托真正执行时,外层的scope已经被释放,导致依赖的MyContext被Disposed,触发报错。改成SingleInstance虽然解决了报错,但引入了内存泄漏风险。你的修复思路——把生命周期范围的创建移到调度委托内部——完全正确,现在只需要优化那两个点:
1. 自动扫描所有IJob实现类型
我们可以通过反射自动扫描程序集,找到所有实现IJob接口的非抽象类,不用手动维护任务列表。如果你的IJob实现都在当前程序集里,用以下代码即可;如果分布在其他程序集,只需扩展扫描的程序集范围:
// 扫描当前程序集所有实现IJob的非抽象类 var jobTypes = Assembly.GetExecutingAssembly() .GetTypes() .Where(t => typeof(IJob).IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface) .ToList();
如果需要扫描多个程序集:
// 示例:扫描当前程序集和指定的Job程序集 var assemblies = new[] { Assembly.GetExecutingAssembly(), Assembly.Load("Your.Jobs.Assembly.Name") }; var jobTypes = assemblies.SelectMany(a => a.GetTypes()) .Where(t => typeof(IJob).IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface) .ToList();
2. 根据Job类型解析对应的实例
首先要确保Autofac已经自动注册了所有IJob实现,这样才能按类型正确解析。先完善你的Autofac注册逻辑:
第一步:完善Autofac依赖注册
在注册MyContext的代码块中,添加自动注册所有IJob实现的逻辑:
private static void RegisterDependencies(ContainerBuilder builder) { // 注册MyContext,保持InstancePerLifetimeScope避免内存泄漏 builder.Register(c => new MyContext(GetConnectionString())) .InstancePerLifetimeScope(); // 自动注册所有IJob实现,每个实例按生命周期范围创建 builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()) .Where(t => typeof(IJob).IsAssignableFrom(t)) .As<IJob>() .InstancePerLifetimeScope(); }
第二步:修改Start方法,按类型解析Job
现在在Start方法中,遍历自动扫描到的jobTypes,在调度委托内部创建独立的生命周期范围,并解析对应类型的Job实例:
public void Start() { // 自动扫描所有IJob实现类型 var jobTypes = Assembly.GetExecutingAssembly() .GetTypes() .Where(t => typeof(IJob).IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface) .ToList(); foreach (var jobType in jobTypes) { // 获取当前Job的调度间隔(这里提供两种常见实现方式) var interval = GetJobInterval(jobType); _schedule.Every(interval, () => { using (var scope = _lifetimeScope.BeginLifetimeScope()) { // 解析对应类型的Job实例 var job = (IJob)scope.Resolve(jobType); job.Run(); } }); } } // 方式1:通过自定义Attribute获取间隔(推荐,更优雅) private TimeSpan GetJobInterval(Type jobType) { var intervalAttr = jobType.GetCustomAttribute<JobIntervalAttribute>(); return intervalAttr?.Interval ?? TimeSpan.FromSeconds(30); } // 自定义Attribute示例 [AttributeUsage(AttributeTargets.Class)] public class JobIntervalAttribute : Attribute { public TimeSpan Interval { get; } public JobIntervalAttribute(int seconds) { Interval = TimeSpan.FromSeconds(seconds); } } // 使用Attribute标记Job的间隔 [JobInterval(30)] public class ExpiryDateTask : IJob { // ... Job实现代码 }
关键注意点
- 把
BeginLifetimeScope放在调度委托内部,确保每次Job执行时都有独立的生命周期范围,MyContext会在scope结束时自动释放,既避免了DbContext被Disposed的错误,又不会导致内存泄漏。 - 自动扫描+注册的方式让你新增
IJob实现时,不需要手动修改Start方法,代码扩展性大幅提升。 - 用自定义Attribute标记调度间隔,比通过Job实例获取间隔更高效,避免创建临时实例。
内容的提问来源于stack exchange,提问作者DiPix
相关产品推荐
相关产品推荐

