.NET 6控制台应用依赖注入:构造传参与属性初始化方案
.NET 6控制台应用依赖注入问题解答
问题背景
我有一个.NET 6控制台应用,其中包含依赖于IDatabase的Product类,代码如下:
Product.cs
public class Product { private readonly IDatabase databaseConnection; public string Name { get; set; } public decimal Price { get; set; } public string Category { get; set; } private int StockQuantity { get; set; } public Product(string name, decimal price, string category, int stockQuantity, IDatabase databaseConnection) { this.databaseConnection = databaseConnection; //Id = productId; Name = name; Price = price; Category = category; StockQuantity = stockQuantity; CreateProduct(); } //some code }
现有两个问题:
- 如何编写正确使用依赖注入(DI)的Program.cs?
- 若不想通过构造函数初始化属性,更优的实现方式是什么?
当前我的Program.cs代码存在错误,因Product需要已注入容器的IDatabase依赖,代码如下:
Program.cs(错误实现)
var host = CreateHostBuilder(args).Build(); host.Run(); static IHostBuilder CreateHostBuilder(string[] args) { return Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); }) .ConfigureServices((hostContext, services) => { string connectionString = hostContext.Configuration.GetConnectionString("DefaultConnection"); services.AddSingleton(new DatabaseConnection(connectionString)); services.AddTransient(new Product("",4,"",4)); }); }
我尝试了一种替代方案,通过方法声明属性解决了DI问题,但不确定如何解决原问题及哪种方案更优:
替代方案代码
var host = CreateHostBuilder(args).Build(); var product = host.Services.GetRequiredService<Product>(); product.SetProductDetails(1, "Sample Product", 49.99m, "Electronics", 100); host.Run();
问题解答
1. 正确使用依赖注入的Program.cs实现
错误原因是直接实例化Product添加到服务容器,DI容器无法自动注入IDatabase依赖。正确做法是让容器负责创建Product实例,通过泛型注册让容器自动解析依赖:
var host = CreateHostBuilder(args).Build(); // 如需使用Product,在此处从容器获取 var product = host.Services.GetRequiredService<Product>(); host.Run(); static IHostBuilder CreateHostBuilder(string[] args) { return Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); }) .ConfigureServices((hostContext, services) => { string connectionString = hostContext.Configuration.GetConnectionString("DefaultConnection"); // 注册IDatabase接口与实现类,确保容器能解析该依赖 services.AddSingleton<IDatabase>(new DatabaseConnection(connectionString)); // 注册Product,容器会自动注入IDatabase实例 services.AddTransient<Product>(); }); }
注意:如果Product的构造函数需要动态业务参数(如名称、价格),直接注册Product到容器并不合适,这类参数属于业务数据而非依赖服务,此时应使用工厂模式处理。
2. 不通过构造函数初始化属性的更优实现
构造函数应仅负责注入依赖服务(如IDatabase),业务数据(名称、价格等)放在构造函数会导致职责混乱,也不利于DI容器创建实例。推荐两种实现方式:
方式一:工厂类封装创建逻辑
创建ProductFactory注入IDatabase,提供专门的方法创建Product,分离依赖注入与业务数据初始化:
// ProductFactory类 public class ProductFactory { private readonly IDatabase _database; public ProductFactory(IDatabase database) { _database = database; } public Product CreateProduct(string name, decimal price, string category, int stockQuantity) { var product = new Product(_database) { Name = name, Price = price, Category = category, StockQuantity = stockQuantity }; product.CreateProduct(); return product; } } // 修改后的Product类 public class Product { private readonly IDatabase databaseConnection; public string Name { get; set; } public decimal Price { get; set; } public string Category { get; set; } private int StockQuantity { get; set; } // 构造函数仅注入依赖服务 public Product(IDatabase databaseConnection) { this.databaseConnection = databaseConnection; } public void CreateProduct() { // 原有创建逻辑 } }
在Program.cs中注册工厂:
services.AddSingleton<IDatabase>(new DatabaseConnection(connectionString)); services.AddTransient<ProductFactory>();
使用时:
var host = CreateHostBuilder(args).Build(); var factory = host.Services.GetRequiredService<ProductFactory>(); var product = factory.CreateProduct("Sample Product", 49.99m, "Electronics", 100); host.Run();
方式二:初始化方法规范状态设置
保留依赖注入构造函数,提供初始化方法一次性设置业务数据,避免实例状态被随意修改:
// 修改后的Product类 public class Product { private readonly IDatabase databaseConnection; private bool _isInitialized = false; public string Name { get; private set; } public decimal Price { get; private set; } public string Category { get; private set; } private int StockQuantity { get; set; } public Product(IDatabase databaseConnection) { this.databaseConnection = databaseConnection; } public void Initialize(string name, decimal price, string category, int stockQuantity) { if (_isInitialized) throw new InvalidOperationException("Product已初始化,不可重复设置"); Name = name; Price = price; Category = category; StockQuantity = stockQuantity; CreateProduct(); _isInitialized = true; } public void CreateProduct() { // 原有创建逻辑 } }
使用时:
var host = CreateHostBuilder(args).Build(); var product = host.Services.GetRequiredService<Product>(); product.Initialize("Sample Product", 49.99m, "Electronics", 100); host.Run();
方案对比
- 你的替代方案可正常工作,但如果
Product属性为可写状态,可能导致实例状态被随意修改,建议将业务属性设为私有setter,通过初始化方法一次性设置。 - 工厂模式适合复杂对象创建逻辑,扩展性更强,尤其当
Product有多种创建方式时。 - 初始化方法更简单直接,适合创建逻辑不复杂的场景。
内容的提问来源于stack exchange,提问作者Vijul
相关产品推荐
相关产品推荐

