如何移除HangFire项目与DataService项目之间的循环依赖
如何移除HangFire项目与DataService项目之间的循环依赖
兄弟,我看你这情况是典型的项目依赖拧麻花了——Hangfire要写DataService里的AuditLog,DataService又要调Hangfire的任务,互相引用直接炸出循环依赖了。别慌,咱们用共享接口+中间类库的思路就能拆解开,一步步来:
第一步:抽离共享内容到独立类库
先新建一个独立的类库项目(比如叫App.Shared或者App.Domain),把两个项目都需要的核心内容移进去:
- 把
AuditLog实体类迁移到这个类库,这样Hangfire和DataService都能直接使用该实体,不用再互相引用对方的项目。 - 定义两个抽象接口,分别把双方需要对外暴露的能力封装起来:
// 给DataService用的任务调度接口,不用关心具体实现是Hangfire还是别的框架 public interface IJobScheduler { void JobLogUpdate<T>(T origin, T newValues, string user) where T : class; } // 给Hangfire用的审计日志写入接口,不用关心具体是哪个DbContext在操作 public interface IAuditLogWriter { Task WriteAuditLogAsync<T>(T origin, T newValues, string user) where T : class; }
第二步:改造Hangfire项目
Hangfire项目现在只需要引用App.Shared类库,彻底移除对DataService的依赖:
- 让
HangFireService实现IJobScheduler接口,构造函数注入IAuditLogWriter(不再直接拿DataService的DbContext)。 - 把原来直接操作DbContext写AuditLog的逻辑,改成调用
IAuditLogWriter的方法:public class HangFireService : IJobScheduler { private readonly IBackgroundJobClient _jobClient; private readonly IAuditLogWriter _auditLogWriter; // 注入审计日志写入器,不用再依赖DataService的上下文 public HangFireService(IBackgroundJobClient jobClient, IAuditLogWriter auditLogWriter) { _jobClient = jobClient; _auditLogWriter = auditLogWriter; } public void JobLogUpdate<T>(T origin, T newValues, string user) where T : class { _jobClient.Enqueue(() => _auditLogWriter.WriteAuditLogAsync(origin, newValues, user)); } }
第三步:改造DataService项目
DataService同样只引用App.Shared类库,移除对Hangfire项目的依赖:
- 实现
IAuditLogWriter接口,把原来写AuditLog的逻辑移到这里(本来DataService就负责DB操作,这部分逻辑归它很合理):public class AuditLogWriter : IAuditLogWriter { private readonly YourMainDbContext _context; public AuditLogWriter(YourMainDbContext context) { _context = context; } public async Task WriteAuditLogAsync<T>(T origin, T newValues, string user) where T : class { if (origin.GetType() != newValues.GetType()) { throw new InvalidOperationException("Objects of different Type"); } var audit = new AuditLog() { Id = Guid.NewGuid(), ChangedBy = user, ChangedDate = DateTime.Now, Original = JsonSerializer.Serialize(origin), NewValue = JsonSerializer.Serialize(newValues), ObjectName = typeof(T).Name }; _context.Add(audit); await _context.SaveChangesAsync(); } } - 在DataService的业务类里,不再直接new
HangFireService,而是通过依赖注入IJobScheduler接口来调用方法:// 比如这是DataService里的Booking业务类 public class BookingService { private readonly YourMainDbContext _context; private readonly IJobScheduler _jobScheduler; // 注入任务调度器接口,不用关心具体实现 public BookingService(YourMainDbContext context, IJobScheduler jobScheduler) { _context = context; _jobScheduler = jobScheduler; } public void UpdateBooking(Booking bookingOrig, Booking bookingNew, string user) { // 你的核心业务逻辑... // 调用调度接口触发审计日志任务 _jobScheduler.JobLogUpdate(bookingOrig, bookingNew, user); } }
第四步:在启动项目里注册服务
最后在你的Web项目或者启动入口(比如ASP.NET Core的Program.cs),把两个服务的实现注册到DI容器里:
// 注册审计日志写入器 builder.Services.AddScoped<IAuditLogWriter, AuditLogWriter>(); // 注册Hangfire实现的任务调度器 builder.Services.AddScoped<IJobScheduler, HangFireService>(); // 别忘了注册Hangfire本身的服务 builder.Services.AddHangfire(config => config.UseSqlServerStorage("你的Hangfire数据库连接字符串")); builder.Services.AddHangfireServer();
这样一来,Hangfire和DataService就彻底解耦了:它们都只依赖中间的共享类库,互相之间没有直接引用,循环依赖的问题解决了,而且以后要替换任务调度框架,或者修改AuditLog的存储逻辑,都不用动对方的代码,扩展性也更强。
备注:内容来源于stack exchange,提问作者bilpor
相关产品推荐
相关产品推荐

