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

.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
}

现有两个问题:

  1. 如何编写正确使用依赖注入(DI)的Program.cs?
  2. 若不想通过构造函数初始化属性,更优的实现方式是什么?

当前我的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 02:52:16