You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何移除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的依赖:

  1. 让HangFireService实现IJobScheduler接口,构造函数注入IAuditLogWriter(不再直接拿DataService的DbContext)。
  2. 把原来直接操作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项目的依赖:

  1. 实现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();
        }
    }
    
  2. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.21 07:29:30