请教:我对Microsoft.Extensions.DependencyInjection的使用理解是否正确?
关于Microsoft.Extensions.DependencyInjection的使用理解修正与说明
你的核心思路是对的——用DI框架统一注入默认SqlConnection来减少重复样板代码,确实能解决30个类都要手动传默认连接的麻烦。但代码里有几个细节问题需要调整才能正常工作,具体如下:
1. SqlConnection注册的语法错误
你写的services.AddTransient<SqlConnection, MySensibleDefaultSqlConnection()>();是错误的,正确的注册方式分两种情况:
- 如果
MySensibleDefaultSqlConnection是SqlConnection的子类,直接注册类型即可:services.AddTransient<SqlConnection, MySensibleDefaultSqlConnection>(); - 如果
MySensibleDefaultSqlConnection是一个创建SqlConnection的方法(比如静态工厂),用工厂委托注册:services.AddTransient<SqlConnection>(_ => MySensibleDefaultSqlConnection());
2. 混合参数的传递问题
ServiceProvider.GetRequiredService<SqlClass1>("foo")这种写法行不通,因为MSDI的GetRequiredService不支持直接传递构造函数的非注入参数。针对这种既有注入依赖(SqlConnection)又有动态参数(比如UserName)的场景,推荐两种解决方案:
方案一:使用ActivatorUtilities.CreateInstance
这个工具类可以结合DI容器中的服务和手动传入的参数来创建实例,正好匹配你的需求:
// 替代原来的GetRequiredService调用 var x = ActivatorUtilities.CreateInstance<SqlClass1>(ServiceProvider, "foo");
方案二:注册工厂委托
如果需要更灵活的控制,可以为每个类注册工厂:
// 在ConfigureServices中注册SqlClass1的工厂 services.AddTransient<Func<string, SqlClass1>>(sp => userName => new SqlClass1(userName, sp.GetRequiredService<SqlConnection>())); // 使用时直接获取工厂并调用 var sqlClass1Factory = ServiceProvider.GetRequiredService<Func<string, SqlClass1>>(); var x = sqlClass1Factory("foo");
修正后的完整代码示例
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; static class Program { public static IServiceProvider ServiceProvider { get; private set; } static IHostBuilder CreateHostBuilder() { return Host.CreateDefaultBuilder().ConfigureServices((context, services) => { // 注册默认SqlConnection(假设MySensibleDefaultSqlConnection是SqlConnection子类) services.AddTransient<SqlConnection, MySensibleDefaultSqlConnection>(); // 注册需要注入的类 services.AddTransient<SqlClass1>(); services.AddTransient<SqlClass2>(); // [...] services.AddTransient<SqlClass30>(); }); } static void Main() { var host = CreateHostBuilder().Build(); ServiceProvider = host.Services; // 使用ActivatorUtilities创建带动态参数的实例 var x = ActivatorUtilities.CreateInstance<SqlClass1>(ServiceProvider, "foo"); // 对比原来的手动写法:var x = new SqlClass1("foo", new MySensibleDefaultSqlConnection()); } } // 示例类定义 public class MySensibleDefaultSqlConnection : SqlConnection { public MySensibleDefaultSqlConnection() : base("your-default-connection-string") { } } public class SqlClass1 { public SqlClass1(string userName, SqlConnection conn) { // 构造逻辑 } } public class SqlClass2 { public SqlClass2(string userId, SqlConnection conn) { // 构造逻辑 } }
总结
你的核心想法完全正确——DI确实能帮你省去为每个类写默认构造函数的重复工作,统一管理依赖的创建逻辑,也方便单元测试时替换SqlConnection为模拟实现。只需要修正注册语法和动态参数的传递方式,代码就能正常运行。
内容的提问来源于stack exchange,提问作者J. Mini
相关产品推荐
相关产品推荐

