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

C#依赖注入中避免循环依赖并跨不可引用程序集调用方法

解决C#跨程序集调用的循环依赖与无直接引用调用问题

一、避免循环依赖的核心思路:依赖倒置+中间抽象层

循环依赖的根源是领域层与应用层互相引用,解决的核心是依赖倒置原则:

  • 新建独立的共享类库(比如Shared.Contracts),存放双方都需要的抽象接口、事件定义。
  • 领域层只引用这个共享类库,依赖抽象而非应用层的具体实现。
  • 应用层引用共享类库和领域层,实现抽象接口或处理领域事件。

二、替代反射的最优方案:领域事件驱动

你当前用反射的方式存在类型不安全、性能差、维护难的问题,用领域事件是更符合架构设计的方案:

步骤1:在共享类库定义事件与处理器抽象

// Shared.Contracts/IDomainEvent.cs
public interface IDomainEvent { }

// Shared.Contracts/SomeEntityCreatedEvent.cs
public class SomeEntityCreatedEvent : IDomainEvent
{
    public SomeEntity Entity { get; }
    public SomeEntityCreatedEvent(SomeEntity entity) => Entity = entity;
}

// Shared.Contracts/IEventHandler.cs
public interface IEventHandler<TEvent> where TEvent : IDomainEvent
{
    Task HandleAsync(TEvent domainEvent);
}

// Shared.Contracts/IEventPublisher.cs
public interface IEventPublisher
{
    Task PublishAsync<TEvent>(TEvent domainEvent) where TEvent : IDomainEvent;
}

步骤2:领域层发布事件(不依赖应用层)

修改领域服务,注入抽象的事件发布器,只负责发布事件,无需关心谁处理:

// Domain/SomeDomainService.cs
public class SomeDomainService<T> : ISomeDomainService<T>
{
    private readonly IEventPublisher _eventPublisher;

    public SomeDomainService(IEventPublisher eventPublisher)
    {
        _eventPublisher = eventPublisher;
    }

    public async Task<SomeEntity> SomeProcess(SomeEntity someEntity)
    {
        await Task.Delay(1000);

        someEntity.Id = new Random().Next(3000);
        someEntity.Name = $"Entity {someEntity.Id}";
        someEntity.Description = "lorem ipsum";

        // 发布事件,彻底解耦与应用层的直接依赖
        await _eventPublisher.PublishAsync(new SomeEntityCreatedEvent(someEntity));

        return someEntity;
    }
}

步骤3:应用层实现事件处理器

应用层实现事件处理器,处理领域层发布的事件,同时调用内部服务:

// Application/SomeEntityCreatedEventHandler.cs
public class SomeEntityCreatedEventHandler : IEventHandler<SomeEntityCreatedEvent>
{
    private readonly IAnotherApplicationService<Message> _anotherService;

    public SomeEntityCreatedEventHandler(IAnotherApplicationService<Message> anotherService)
    {
        _anotherService = anotherService;
    }

    public async Task HandleAsync(SomeEntityCreatedEvent domainEvent)
    {
        var entity = domainEvent.Entity;
        var domainMessage = new Message
        {
            Id = entity.Id.ToString(),
            Title = $"Some entity {entity.Name}",
            Body = "Message from Domain"
        };

        await _anotherService.SendSomeMessage(domainMessage);
    }
}

// Application/EventPublisher.cs
public class EventPublisher : IEventPublisher
{
    private readonly IServiceProvider _serviceProvider;

    public EventPublisher(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public async Task PublishAsync<TEvent>(TEvent domainEvent) where TEvent : IDomainEvent
    {
        // 从DI容器获取所有对应事件的处理器并执行
        var handlers = _serviceProvider.GetServices<IEventHandler<TEvent>>();
        foreach (var handler in handlers)
        {
            await handler.HandleAsync(domainEvent);
        }
    }
}

步骤4:DI容器注册所有服务

在启动项目(比如Web项目)中完成依赖注入注册:

// Program.cs
builder.Services.AddScoped<ISomeDomainService<SomeEntity>, SomeDomainService<SomeEntity>>();
builder.Services.AddScoped<IAnotherApplicationService<Message>, AnotherApplicationService<Message>>();
builder.Services.AddScoped<IEventHandler<SomeEntityCreatedEvent>, SomeEntityCreatedEventHandler>();
builder.Services.AddScoped<IEventPublisher, EventPublisher>();

三、非事件场景的直接调用方案

如果必须直接调用某个服务,同样用依赖倒置解耦:

  1. 在共享类库定义服务抽象:
// Shared.Contracts/IMessageSender.cs
public interface IMessageSender
{
    Task SendMessageAsync(Message message);
}
  1. 应用层实现这个抽象:
// Application/MessageSender.cs
public class MessageSender : IMessageSender
{
    private readonly IAnotherApplicationService<Message> _anotherService;

    public MessageSender(IAnotherApplicationService<Message> anotherService)
    {
        _anotherService = anotherService;
    }

    public async Task SendMessageAsync(Message message)
    {
        await _anotherService.SendSomeMessage(message);
    }
}
  1. 领域层注入抽象服务直接调用:
// Domain/SomeDomainService.cs
public class SomeDomainService<T> : ISomeDomainService<T>
{
    private readonly IMessageSender _messageSender;

    public SomeDomainService(IMessageSender messageSender)
    {
        _messageSender = messageSender;
    }

    public async Task<SomeEntity> SomeProcess(SomeEntity someEntity)
    {
        // ... 原有逻辑

        var domainMessage = new Message
        {
            Id = someEntity.Id.ToString(),
            Title = $"Some entity {someEntity.Name}",
            Body = "Message from Domain"
        };

        await _messageSender.SendMessageAsync(domainMessage);

        return someEntity;
    }
}
  1. DI注册:
builder.Services.AddScoped<IMessageSender, MessageSender>();

为什么反射方案不是最优?

  • 类型不安全:硬编码程序集、类名,变更后会触发运行时错误,编译期无法检测。
  • 性能损耗:反射调用比直接接口调用慢,频繁调用场景下影响明显。
  • 维护困难:代码可读性差,后续调试、修改成本高。

内容的提问来源于stack exchange,提问作者Milton Calderon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 04:12:48