是 Asp.Net Core 2.0 中的配置对象,可注入到 class 库项目中
Is the Configuration object in Asp.Net Core 2.0, injectable in a class library project
我在 Class 库中设置 classes 没有问题,在 Startup.cs 中的服务中将它们设置为可注入并让它们按预期工作。
在我正在使用的测试应用程序中。我有一个数据 class 库项目和我的 MVC 应用程序项目(Core 2.0.1 和 EF 2.0.1 和 MVC6)
在Startup.cs
// *If* you need access to generic IConfiguration this is **required**
services.AddSingleton<IConfiguration>(Configuration);
services.AddOptions();
// Add our class objects so they can be injected
services.Configure<TestAppServices.ConfigurationClasses.ApplicationSettings>(Configuration.GetSection("ApplicationSettings"));
services.Configure<TestAppServices.ConfigurationClasses.LoggingSettings>(Configuration.GetSection("Logging"));
services.Configure<TestAppServices.ConfigurationClasses.ApplicationIconUrlSettings>(Configuration.GetSection("ApplicationIconUrls"));
services.Configure<TestAppServices.ConfigurationClasses.DatabaseConfigurationSettings>(Configuration.GetSection("ConnectionStrings"));
services.Configure<TestAppServices.ConfigurationClasses.SmsSettings>(Configuration.GetSection("SmsSettings"));
我可以访问已设置的个人 class 中的所有设置,但以下内容不起作用。
private static IConfiguration _configuration { get; set; }
private const string AppSettingsConnectionsHeader = "ConnectionStrings";
public DatabaseHelpers(IConfiguration configuration)
{
_configuration = configuration;
}
public static ConnectionStrings GetConnections()
{
var cs = new ConnectionStrings();
_configuration.GetSection(AppSettingsConnectionsHeader).Bind(cs);
return cs;
}
我遇到了常见的错误
System.NullReferenceException: Object reference not set to an instance of an object. at TestAppServices.DatabaseHelpers.GetConnections()
_configuration 为空,因此不会被注入。
GetConnections 方法是从控制器中调用的,就像这样
var cs = DatabaseHelpers.GetConnections();
是否有一种特殊的方法来设置实际的配置对象,以便它可以注入 class 库项目?
更新:
根据 serpent5 的回答,我将 class 库项目重新配置为以下内容
public static class ConnectionStrings
{
public static string DefaultConnection => "DefaultConnection";
public static string TestingConnection => "TestingConnection";
public static string StagingConnection => "StagingConnection";
public static string ProductionConnection => "ProductionConnection";
public static string DevelopmentConnection => "DevelopmentConnection";
}
public class ConfigurationSettings
{
private readonly IConfiguration _configuration;
public ConfigurationSettings(IConfiguration config)
{
_configuration = config;
}
public DatabaseConfigurationSettings GetConnectionStringFor(DatabaseIsFor dbfor)
{
var dbcs = new DatabaseConfigurationSettings();
switch (dbfor)
{
case DatabaseIsFor.Development:
if (!string.IsNullOrEmpty(_configuration.GetConnectionString(ConnectionStrings.DevelopmentConnection)))
{
dbcs.CompleteConnectionString = _configuration.GetConnectionString(ConnectionStrings.DevelopmentConnection);
dbcs.ConnectionName = ConnectionStrings.DevelopmentConnection; //== _connections.Value.DevelopmentConnection.ToString();
return dbcs;
}
break;
case DatabaseIsFor.Testing:
if (!string.IsNullOrEmpty(_configuration.GetConnectionString(ConnectionStrings.TestingConnection)))
{
dbcs.CompleteConnectionString = _configuration.GetConnectionString(ConnectionStrings.TestingConnection);
dbcs.ConnectionName = ConnectionStrings.TestingConnection; //== _connections.Value.TestingConnection.ToString();
return dbcs;
}
break;
case DatabaseIsFor.Staging:
if (!string.IsNullOrEmpty(_configuration.GetConnectionString(ConnectionStrings.StagingConnection)))
{
dbcs.CompleteConnectionString = _configuration.GetConnectionString(ConnectionStrings.StagingConnection);
dbcs.ConnectionName = ConnectionStrings.StagingConnection; //== _connections.Value.TestingConnection.ToString();
return dbcs;
}
break;
case DatabaseIsFor.Production:
if (!string.IsNullOrEmpty(_configuration.GetConnectionString(ConnectionStrings.ProductionConnection)))
{
dbcs.CompleteConnectionString = _configuration.GetConnectionString(ConnectionStrings.ProductionConnection);
dbcs.ConnectionName = ConnectionStrings.ProductionConnection; //== _connections.Value.TestingConnection.ToString();
return dbcs;
}
break;
}
dbcs.CompleteConnectionString = _configuration.GetConnectionString(ConnectionStrings.DefaultConnection);
dbcs.ConnectionName = ConnectionStrings.DefaultConnection; //== _connections.Value.DefaultConnection.ToString();
return dbcs;
}
}
而我在controller中使用如下
public class HomeController : Controller
{
private IConfiguration Configuration { get; set; }
public HomeController(IConfiguration configuration)
{
Configuration = configuration;
}
public IActionResult Index()
{
// test view to check that this works
var cs = new ConfigurationSettings(Configuration);
var dbcs = cs.GetConnectionStringFor(DatabaseIsFor.Production);
var svm = SettingsViewModel(dbcs);
return View(svm);
}
}
它工作得很好,除了 我不明白为什么我在更新 ConfigurationSettings 时必须通过 Configuration。自从我在 Startup.cs ConfigureServices 中添加它以来,它不应该在那里可用吗?
// Access to generic IConfiguration
services.AddSingleton(Configuration);
您不能使用 Constructor Injection if you're not going to instantiate an instance of your class. In the code you've provided, you've set up DatabaseHelpers
to have an instance of IConfiguration
injected via its constructor, but the class itself does not take part in Dependency Injection,因此永远不需要调用其构造函数。
针对您的特定示例的一种解决方法是以某种方式显式初始化您的静态 class。您可以向 DatabaseHelpers
添加一个新方法,如下所示:
public static Initialize(IConfiguration configuration)
{
_configuration = configuration;
}
然后您只需在应用程序启动时的某处进行设置 - 也许在 ConfigureServices
中。例如:
services.AddSingleton<IConfiguration>(Configuration);
DatabaseHelpers.Initialize(Configuration);
尽管这是针对您的问题的技术解决方法,但我鼓励您考虑为此使用静态助手是否是最佳选择。我假设您 DatabaseHelpers
的实际实现比您在此处显示的更复杂,并且您不能只将 ConnectionStrings
的实例添加到您的依赖注入服务集合并使用它.
更新:依赖注入实际上并不是魔术,它只是一个pattern。当您将创建 classes 的责任委托给其他实体(通常是 MVC 世界中的控制器激活过程)时,它工作得很好,但当您自己控制创建时,它就不能工作了。在您更新的示例中,您对 ConfigurationSettings
的创建承担全部责任;因此,您必须提供其构造函数参数。
如果您不想自己处理,可以将 ConfigurationSettings
添加到您的服务集合中,让 DI 容器为您解析相关的 Configuration
。
我在 Class 库中设置 classes 没有问题,在 Startup.cs 中的服务中将它们设置为可注入并让它们按预期工作。
在我正在使用的测试应用程序中。我有一个数据 class 库项目和我的 MVC 应用程序项目(Core 2.0.1 和 EF 2.0.1 和 MVC6)
在Startup.cs
// *If* you need access to generic IConfiguration this is **required**
services.AddSingleton<IConfiguration>(Configuration);
services.AddOptions();
// Add our class objects so they can be injected
services.Configure<TestAppServices.ConfigurationClasses.ApplicationSettings>(Configuration.GetSection("ApplicationSettings"));
services.Configure<TestAppServices.ConfigurationClasses.LoggingSettings>(Configuration.GetSection("Logging"));
services.Configure<TestAppServices.ConfigurationClasses.ApplicationIconUrlSettings>(Configuration.GetSection("ApplicationIconUrls"));
services.Configure<TestAppServices.ConfigurationClasses.DatabaseConfigurationSettings>(Configuration.GetSection("ConnectionStrings"));
services.Configure<TestAppServices.ConfigurationClasses.SmsSettings>(Configuration.GetSection("SmsSettings"));
我可以访问已设置的个人 class 中的所有设置,但以下内容不起作用。
private static IConfiguration _configuration { get; set; }
private const string AppSettingsConnectionsHeader = "ConnectionStrings";
public DatabaseHelpers(IConfiguration configuration)
{
_configuration = configuration;
}
public static ConnectionStrings GetConnections()
{
var cs = new ConnectionStrings();
_configuration.GetSection(AppSettingsConnectionsHeader).Bind(cs);
return cs;
}
我遇到了常见的错误
System.NullReferenceException: Object reference not set to an instance of an object. at TestAppServices.DatabaseHelpers.GetConnections()
_configuration 为空,因此不会被注入。
GetConnections 方法是从控制器中调用的,就像这样
var cs = DatabaseHelpers.GetConnections();
是否有一种特殊的方法来设置实际的配置对象,以便它可以注入 class 库项目?
更新:
根据 serpent5 的回答,我将 class 库项目重新配置为以下内容
public static class ConnectionStrings
{
public static string DefaultConnection => "DefaultConnection";
public static string TestingConnection => "TestingConnection";
public static string StagingConnection => "StagingConnection";
public static string ProductionConnection => "ProductionConnection";
public static string DevelopmentConnection => "DevelopmentConnection";
}
public class ConfigurationSettings
{
private readonly IConfiguration _configuration;
public ConfigurationSettings(IConfiguration config)
{
_configuration = config;
}
public DatabaseConfigurationSettings GetConnectionStringFor(DatabaseIsFor dbfor)
{
var dbcs = new DatabaseConfigurationSettings();
switch (dbfor)
{
case DatabaseIsFor.Development:
if (!string.IsNullOrEmpty(_configuration.GetConnectionString(ConnectionStrings.DevelopmentConnection)))
{
dbcs.CompleteConnectionString = _configuration.GetConnectionString(ConnectionStrings.DevelopmentConnection);
dbcs.ConnectionName = ConnectionStrings.DevelopmentConnection; //== _connections.Value.DevelopmentConnection.ToString();
return dbcs;
}
break;
case DatabaseIsFor.Testing:
if (!string.IsNullOrEmpty(_configuration.GetConnectionString(ConnectionStrings.TestingConnection)))
{
dbcs.CompleteConnectionString = _configuration.GetConnectionString(ConnectionStrings.TestingConnection);
dbcs.ConnectionName = ConnectionStrings.TestingConnection; //== _connections.Value.TestingConnection.ToString();
return dbcs;
}
break;
case DatabaseIsFor.Staging:
if (!string.IsNullOrEmpty(_configuration.GetConnectionString(ConnectionStrings.StagingConnection)))
{
dbcs.CompleteConnectionString = _configuration.GetConnectionString(ConnectionStrings.StagingConnection);
dbcs.ConnectionName = ConnectionStrings.StagingConnection; //== _connections.Value.TestingConnection.ToString();
return dbcs;
}
break;
case DatabaseIsFor.Production:
if (!string.IsNullOrEmpty(_configuration.GetConnectionString(ConnectionStrings.ProductionConnection)))
{
dbcs.CompleteConnectionString = _configuration.GetConnectionString(ConnectionStrings.ProductionConnection);
dbcs.ConnectionName = ConnectionStrings.ProductionConnection; //== _connections.Value.TestingConnection.ToString();
return dbcs;
}
break;
}
dbcs.CompleteConnectionString = _configuration.GetConnectionString(ConnectionStrings.DefaultConnection);
dbcs.ConnectionName = ConnectionStrings.DefaultConnection; //== _connections.Value.DefaultConnection.ToString();
return dbcs;
}
}
而我在controller中使用如下
public class HomeController : Controller
{
private IConfiguration Configuration { get; set; }
public HomeController(IConfiguration configuration)
{
Configuration = configuration;
}
public IActionResult Index()
{
// test view to check that this works
var cs = new ConfigurationSettings(Configuration);
var dbcs = cs.GetConnectionStringFor(DatabaseIsFor.Production);
var svm = SettingsViewModel(dbcs);
return View(svm);
}
}
它工作得很好,除了 我不明白为什么我在更新 ConfigurationSettings 时必须通过 Configuration。自从我在 Startup.cs ConfigureServices 中添加它以来,它不应该在那里可用吗?
// Access to generic IConfiguration
services.AddSingleton(Configuration);
您不能使用 Constructor Injection if you're not going to instantiate an instance of your class. In the code you've provided, you've set up DatabaseHelpers
to have an instance of IConfiguration
injected via its constructor, but the class itself does not take part in Dependency Injection,因此永远不需要调用其构造函数。
针对您的特定示例的一种解决方法是以某种方式显式初始化您的静态 class。您可以向 DatabaseHelpers
添加一个新方法,如下所示:
public static Initialize(IConfiguration configuration)
{
_configuration = configuration;
}
然后您只需在应用程序启动时的某处进行设置 - 也许在 ConfigureServices
中。例如:
services.AddSingleton<IConfiguration>(Configuration);
DatabaseHelpers.Initialize(Configuration);
尽管这是针对您的问题的技术解决方法,但我鼓励您考虑为此使用静态助手是否是最佳选择。我假设您 DatabaseHelpers
的实际实现比您在此处显示的更复杂,并且您不能只将 ConnectionStrings
的实例添加到您的依赖注入服务集合并使用它.
更新:依赖注入实际上并不是魔术,它只是一个pattern。当您将创建 classes 的责任委托给其他实体(通常是 MVC 世界中的控制器激活过程)时,它工作得很好,但当您自己控制创建时,它就不能工作了。在您更新的示例中,您对 ConfigurationSettings
的创建承担全部责任;因此,您必须提供其构造函数参数。
如果您不想自己处理,可以将 ConfigurationSettings
添加到您的服务集合中,让 DI 容器为您解析相关的 Configuration
。