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

控制器依赖注入:构造函数注入VS属性注入及基类派生类场景问询

Constructor Injection vs Property Injection for Controllers: Which to Choose?

Great question! Let's break down the tradeoffs between these two DI approaches, specifically tailored to your scenario where a BaseController (using property injection) is inherited by multiple controllers.

First, Let's Cover Constructor Injection: The Preferred Approach

Constructor injection is widely considered the best practice for controller dependencies, and here's why:

  • Explicit, Clear Dependencies: Anyone looking at your controller's constructor immediately knows what services it needs to function. No hidden dependencies buried in property getters—this makes your code far more readable and maintainable.
  • Guaranteed Non-Null Dependencies: When you inject dependencies via the constructor, they're resolved before the controller is fully initialized. You can add null checks (like throw new ArgumentNullException) to catch missing dependencies at startup, not halfway through a request when your code tries to use Audit and gets a null.
  • Follows SOLID Principles: It aligns perfectly with the Dependency Inversion Principle—your controller depends on abstractions (like IAuditRepository), and the constructor enforces that these abstractions are provided. It also avoids tight coupling to a specific DI container (like your current use of DependencyResolver.Current).
  • DI Container Friendly: Modern frameworks like ASP.NET Core's built-in DI container natively support constructor injection, handling all the resolution automatically. You won't need to write manual GetService calls, reducing boilerplate and potential bugs.

How to Refactor Your BaseController to Use Constructor Injection

Here's how you'd rewrite your base controller to follow this pattern:

private readonly IAuditRepository _audit;

protected BaseController(IAuditRepository audit)
{
    _audit = audit ?? throw new ArgumentNullException(nameof(audit));
}

protected IAuditRepository Audit => _audit;

Then, your derived controllers simply pass the required dependency up to the base class in their own constructors:

public class ProductController : BaseController
{
    private readonly IProductRepository _productRepo;

    public ProductController(IAuditRepository audit, IProductRepository productRepo)
        : base(audit)
    {
        _productRepo = productRepo ?? throw new ArgumentNullException(nameof(productRepo));
    }
}

When Might Property Injection Make Sense?

Property injection isn't all bad—it has niche use cases:

  • Optional Dependencies: If a service isn't critical to the controller's core functionality (e.g., a logging service that's nice to have but not required), property injection can work. You can set a default value or check for null before using it.
  • Legacy Framework Constraints: In older versions of ASP.NET MVC (pre-Core), constructor injection support was limited, so property injection was a common workaround. But this is irrelevant in modern frameworks.

That said, your IAuditRepository looks like a core dependency (you're using it across multiple controllers via the base class), so property injection is not a good fit here.

Problems with Your Current Property Injection Approach

Your current code uses a lazy-loaded property with DependencyResolver.Current.GetService:

private IAuditRepository _audit; 
protected IAuditRepository Audit { get { return _audit ?? (_audit = DependencyResolver.Current.GetService<IAuditRepository>()); } }

This has a few key issues:

  • Hidden Dependency: Someone reading a derived controller's code won't know it depends on IAuditRepository unless they check the base class.
  • Runtime Null Risk: If DependencyResolver can't resolve IAuditRepository (e.g., a registration mistake), you'll get a null reference exception when the Audit property is first used—this could happen in production, not just during testing.
  • Tight Coupling: You're directly tying your controller to DependencyResolver, making it harder to test (you'd have to mock the resolver instead of just passing a mock IAuditRepository).

Final Recommendation

Stick with constructor injection for your controllers and base controller. It's safer, more maintainable, and aligns with industry best practices. The small amount of extra code in derived controllers is worth it for the clarity and reliability it provides.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:10:36