我有一个小类来获取有关我的几个MVC应用程序的用户的一系列信息。一个最小的可重现示例是:
public class InformationGetter
{
public string GetUserInformation(string connectionStr, string storedProcedureName, int userId)
{
// Do SQL work
return info;
}
}
我正在使用"配置服务"步骤将其注入
services.AddScoped<InformationGetter>
然后在我的课堂上,我只是从 DI 调用它。
现在,显然 connectionStr 和 storedProcedure 只更改每个应用程序,但现在我将其作为参数传递。
我尝试公开这些参数并使用服务对其进行配置。配置,但是当我从控制器调用它时,我得到空值。
services.AddOptions();
services.Configure<InformationGetter>(options =>
{
options.ConnectionString = Configuration.GetSection("Model").GetSection("ConnectionString").Value;
options.StoredProcedureName = "prInformationGetter";
});
我不确定这失败的原因是因为我在原始类上缺少接口还是我无法理解这个概念。
我也想过做类似services.AddInformationGetter(options => {})
的事情,但我的理解是这种模式是实现中间件,而不是专门用于 DI。
我尝试检查文档(learn.microsoft.com(,但我更加困惑。
对所涉及的概念可能存在误解。
Configure<TOption>
将注册IOptions<TOptions>
。现在,您的示例中有两个单独的注册。
注册课程时一次
services.AddScoped<InformationGetter>()
另一个当您注册选项时。
执行以下操作
//..
services.AddOptions();
//Adds IOptions<InformationGetter>
services.Configure<InformationGetter>(options => {
options.ConnectionString = Configuration.GetSection("Model").GetSection("ConnectionString").Value;
options.StoredProcedureName = "prInformationGetter";
});
//Adds InformationGetter but gets it from the registered options
services.AddScoped<InformationGetter>(sp =>
sp.GetRequiredService<IOptions<InformationGetter>>().Value
);
//...
作用域注册将使用工厂委托提取注册的选项并返回所需的类型。
public class InformationGetter {
public string ConnectionString { get; set; }
public string StoredProcedureName { get; set; }
//...
public string GetUserInformation(int userId) {
// Do SQL work
return info;
}
}
InformationGetter
看起来像一项服务。
我建议重构以遵循更多的单一责任原则(SRP(和关注点分离(Soc(设计。
//Needed by InformationGetter to perform its function
public class InformationGetterOptions {
public string ConnectionString { get; set; }
public string StoredProcedureName { get; set; }
}
//abstraction of InformationGetter
public interface IInformationGetter {
string GetUserInformation(int userId);
}
//implementation.
public class InformationGetter : IInformationGetter{
private readonly InformationGetterOptions options;
public InformationGetter(InformationGetterOptions options) {
this.options = options;
}
public string GetUserInformation(int userId) {
//use values in options to connect
// Do SQL work
return info;
}
}
我会完全避免选项模式,而只是使用委托工厂注册类,从配置中提取我需要的东西。这样,您的代码就不会与框架问题紧密耦合,例如IOptions
public void ConfigureServices(IServiceCollection services) {
//...
InformationGetterOptions options = new InformationGetterOptions {
ConnectionString = Configuration.GetSection("Model").GetSection("ConnectionString").Value;
StoredProcedureName = "prInformationGetter";
};
services.AddSingleton(options);
services.AddScoped<IInformationGetter, InformationGetter>();
//...
}
现在,IInformationGetter
可以在需要时注入,并具有执行其功能所需的所有依赖项。