ASP.NET Core——从静态类访问配置
Posted
技术标签:
【中文标题】ASP.NET Core——从静态类访问配置【英文标题】:ASP.NET Core—access Configuration from static class 【发布时间】:2018-02-03 18:04:04 【问题描述】:我想要一个访问配置对象的简单静态类。所有配置信息都已从 Startup 类的 appsettings.json 文件中读取。我只需要一种简单的方法来访问它。这可能吗?
namespace MyNamespace
public static class Config
public string Username => Configuration["Username"];
public string Password => Configuration["Password"];
应用中的其他任何地方:
string username = Config.Username;
string password = Config.Password;
【问题讨论】:
考虑使用依赖倒置作为服务定位器反模式 配置是指 appsettings.json 还是 app.config? appsettings.json。将更新问题。 使用静态类可能是单元测试的坏习惯:***.com/a/38107134/2803565 为什么是静态类?您可以直接注入配置或创建单例 【参考方案1】:基于与上述相同原理的稍短版本...
public Startup(IConfiguration configuration)
Configuration = configuration;
StaticConfig = configuration;
public static IConfiguration StaticConfig get; private set;
在另一个静态类中使用:
string connString = Startup.StaticConfig.GetConnectionString("DefaultConnection");
【讨论】:
在不同的类库之间共享这个配置值会很棘手。想象一下,您在多个项目之间有基础架构横切共享配置/设置? 我很困惑为什么这不是公认的答案。这有什么风险吗?看起来简单实用。 因为它是一种反模式。您应该能够注入事物所需的所有东西,以便在测试期间替换它们。当它是带有private setter
的静态属性时,您将如何替换它?您必须将其设为 public
只是为了进行测试,这是不对的。
我不认为这是一种反模式,因为 IConfiguration 具有绑定方法,允许您将其绑定到一个类,出于这个原因等等。不过,我不会将其存储为 IConfiguration 。反复将字符串解析为它们的真实类型(如布尔值和整数)是我在微服务世界中更关心的一种反模式。当您更改测试值时,您不必设置“StaticConfig”。您可以在测试设置中轻松设置 MyConfigObject.MyProperty = 5
并继续前进。
我相信只要您不在解决方案中使用单独的项目,此解决方案就可以工作。通常,我在解决方案控制器项目、服务项目和repo项目中至少创建3个项目,并将服务项目的引用添加到控制器项目中以使用控制层中的服务。基于此,我无法将控制器项目的引用添加到服务项目以使用 Startup.StaticConfig in-service 项目。它产生循环依赖【参考方案2】:
经过大量研究,这适用于(在 ASPNetCore 2.2 中)从静态类访问 appsettings.json 配置,但由于某种原因 appsettings.development.json 不再正确加载,但它可能是我项目中的其他东西搞砸了. reloadOnChange 确实有效。作为奖励,它还具有 IHostingEnvironment 和 IHttpContextAccessor。虽然这可行,但我最近决定切换回更多的 DI 方法来遵循其他人提到的范式转变。
所以这是在静态类中访问一些 DI 内容(包括配置)的众多方法之一:
AppServicesHelper.cs:
public static class AppServicesHelper
static IServiceProvider services = null;
/// <summary>
/// Provides static access to the framework's services provider
/// </summary>
public static IServiceProvider Services
get return services;
set
if (services != null)
throw new Exception("Can't set once a value has already been set.");
services = value;
/// <summary>
/// Provides static access to the current HttpContext
/// </summary>
public static HttpContext HttpContext_Current
get
IHttpContextAccessor httpContextAccessor = services.GetService(typeof(IHttpContextAccessor)) as IHttpContextAccessor;
return httpContextAccessor?.HttpContext;
public static IHostingEnvironment HostingEnvironment
get
return services.GetService(typeof(IHostingEnvironment)) as IHostingEnvironment;
/// <summary>
/// Configuration settings from appsetting.json.
/// </summary>
public static MyAppSettings Config
get
//This works to get file changes.
var s = services.GetService(typeof(IOptionsMonitor<MyAppSettings>)) as IOptionsMonitor<MyAppSettings>;
MyAppSettings config = s.CurrentValue;
return config;
Startup.cs:
public Startup(IHostingEnvironment env)
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.env.EnvironmentName.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
public void ConfigureServices(IServiceCollection services)
//...
services.AddHttpContextAccessor();//For HttpContext.
// Register the IOptions object
services.Configure<MyAppSettings>(Configuration.GetSection(nameof(MyAppSettings)));
//Explicitly register the settings object by delegating to the IOptions object so that it can be accessed globally via AppServicesHelper.
services.AddSingleton(resolver => resolver.GetRequiredService<IOptionsMonitor<MyAppSettings>>().CurrentValue);
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
//...
AppServicesHelper.Services = app.ApplicationServices;
//...
控制器:
public class MyController: Controller
public MyController()
public MyAppSettings Config => AppServicesHelper.Config;
public async Task<IActionResult> doSomething()
testModel tm = await myService.GetModel(Config.Setting_1);
return View(tm);
另一个类库:
public static class MyLibraryClass
public static string GetMySetting_ => AppServicesHelper.Config.Setting_1;
public static bool IsDev => AppServicesHelper.HostingEnvironment.IsDevelopment();
MyAppSettings.cs 是映射到 appsettings.json 中的 MyAppSettings 部分的任何类:
public class MyAppSettings
public string Setting_1 get;set;
appsettings.json:
"Logging":
"LogLevel":
"Default": "Warning"
,
"AllowedHosts": "*",
"MyAppSettings":
"Setting_1": "something"
【讨论】:
感谢@Soenhay 分享了一个很好的解决方案。我也遇到了同样的问题,并按照您的方法解决了问题。 我还需要类似的东西来从静态类传递存储库引用。你能推荐一些@Soenhay吗??? @NadimHossainSonet 最好将其作为一个单独的问题提出,以获得更完整的答复。一个简短的总结是:您可以使用与上述相同的技术。创建访问存储库的服务类和接口,在ConfigureServices中注册服务,在静态类中通过services.GetService访问。此外,我找不到 DI 方法似乎很少见,因此您可能会重新审视它。【参考方案3】:我同意 mcbowes,它在 docs 中,但第一个示例看起来更像您需要的……想要:
public class Program
public static IConfigurationRoot Configuration get; set;
public static void Main(string[] args = null)
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
Console.WriteLine($"option1 = Configuration["option1"]");
// Edit:
IServiceCollection services = new ServiceCollection();
services.AddOptions();
services.Configure<HelloWorldOptions>(_configuration.GetSection("HelloWorld"));
// And so on...
【讨论】:
是的,但是如何从另一个内部静态类访问Configuration
的这个实例,而不必每次都重新构建它?
那么你应该使用Options
pattern。
@Tubbe 你能提供一个例子来说明它是如何工作的吗?根据我在那篇文章中读到的内容,您仍然需要在构造函数中提供选项,而这在静态类中是不可能的。【参考方案4】:
尽量避免使用静态类并使用 DI
namespace MyNamespace
public interface IConfig
string Username get;
string Password get;
public class Config : IConfig
public Config(IConfiguration configuration)
_configuration = configuration;
readonly IConfiguration _configuration;
public string Username => _configuration["Username"];
public string Password => _configuration["Password"];
StartUp类中的设置DI
public class Startup
public void ConfigureServices(IServiceCollection services)
//...
services.AddTransient<IConfig, Config>();
...
然后像这样使用它
public class TestUsage
public TestUsage(IConfig config)
_config = config;
readonly IConfig _config;
public string Username => _config.Username;
public string Password => _config.Password;
【讨论】:
如果要将 NET Framework 迁移到 NET Core,则必须修改所有使用应用程序设置的类才能注入配置值(IOptions、IConfiguration 或其他)。如果您有一个庞大的项目,编辑类将花费大量时间和测试。 :-O 希望看到一种更简单的方法,无需 DI 和修改类构造函数 为什么有一个静态类这么糟糕?我知道字典访问速度很快,但是当我觉得if(MyStaticClass.Enabled)
对于每秒调用多次的方法来说更快更轻时,我不得不使用 if(configuration["enabled"] == "True")
或 if(configuration.GetValue<int>("enabled"))
让我有点恼火。
^ 完全同意。这种方法(向下滚动到“静态实例”)仍然受到许多开发人员的青睐:weblog.west-wind.com/posts/2017/dec/12/…
尝试AppDomain.CurrentDomain.AssemblyResolve静态事件,通过设置文件路径定义查找dll路径【参考方案5】:
您可以使用Signleton 模式从任何地方访问您的配置
public class ConnectionStrings
private ConnectionStrings()
// property with getter only will not work.
public static ConnectionStrings Instance get; protected set; = new ConnectionStrings();
public string DatabaseConnection get; set;
在你的创业班里
public class Startup
private readonly IConfiguration configuration;
public Startup(IConfiguration configuration)
this.configuration = configuration;
configuration.GetSection("ConnectionStrings").Bind(ConnectionStrings.Instance);
public void ConfigureServices(IServiceCollection services)
public void Configure(IApplicationBuilder app)
【讨论】:
很好的例子。您能否展示如何从 appsettings.json 中检索值以及如何在 SomeController.cs 中使用它 就像连接字符串一样,定义另一个单例类,将其称为 AppSettings 并将特定部分绑定到您的单例类,最后从任何地方调用它,但请注意 appsettings.json 中的任何修改都不会影响您的单例上课,直到您重新启动应用程序... 很酷,谢谢——我当然更喜欢使用整个依赖注入路线,但是在逐步迁移旧项目时,这将帮助我朝着这个方向前进! 这个问题是指静态类,而不是非静态类。【参考方案6】:-
在服务层创建ConfigurationHelper静态类,这样就可以在没有循环依赖的情况下在其他层使用。
public static class ConfigurationHelper
public static IConfiguration config;
public static void Initialize(IConfiguration Configuration)
config = Configuration;
-
在 Startup 类的 ConfigureServices 方法中初始化 ConfigurationHelper。
ConfigurationHelper.Initialize(Configuration);
-
在任何你想要的地方使用它,包括你的静态类
e.g: ConfigurationHelper.config.GetSection("AWS:Accesskey").Value;
【讨论】:
这是 ASP.NET Core 3.1+ 的完美解决方案。我在 .NET 5 上对其进行了测试,效果非常好。感谢分享@pedram 你在 program.cs ConfigurationHelper.Initialize(builder.Configuration) 中节省了我的时间 .net 6;【参考方案7】:这已经说过了,但我要说出来。
我相信 .Net Core 希望开发人员通过依赖注入获得价值。这是我从研究中注意到的,但我也在推测。作为开发人员,我们需要遵循这种范式转变才能很好地使用 .Net Core。
Options Pattern 是静态配置的不错替代品。在你的情况下,它看起来像这样:
appsettings.json
"Username": "MyUsername",
"Password": "Password1234"
SystemUser.cs
public class SystemUser
public string Username get; set; = "";
public string Password get; set; = "";
Startup.cs
services.Configure<SystemUser>(Configuration);
要使用 SystemUser 类,我们执行以下操作。
TestController.cs
public class TestController : Controller
private readonly SystemUser systemUser;
public TestController(IOptionsMonitor<SystemUser> systemUserOptions)
this.systemUser = systemUserOptions.CurrentValue;
public void SomeMethod()
var username = this.systemUser.Username; // "MyUsername"
var password = this.systemUser.Password; // "Password1234"
即使我们没有使用静态类,我认为这是满足您需求的最佳选择。否则,您可能必须在 Startup 类中使用静态属性,这是一个可怕的解决方案。
【讨论】:
静态类不是面向对象编程的最佳实践有点笨拙。你的意思是说 stateful 静态类。但即便如此,也是相当沉重的。这种形式的 DI 可以说更像是一种组件设计概念,而不是 OOP 概念。调用者必须了解内部依赖关系并不真正符合 OOP 理想的精神。开发人员喜欢它的权衡是细粒度的单元测试。我们中的许多人都有极其贫乏的 OOP 设计,它比 OOP 更接近于传递状态的命令式函数。 如果我依赖于公共交通等库并且在配置 Bus 时,我想使用选项模式而不是从 appsettings 访问硬编码字符串,我将如何使用选项模式? 如何解决 AppDomain.CurrentDomain.AssemblyResolve 静态事件【参考方案8】:我个人喜欢这个link使用的方法
本质上它只是向您的选项类添加一个静态字段。
public class WeblogConfiguration
public static WeblogConfiguration Current;
public WeblogConfiguration()
Current = this;
然后在任何静态类中你都可以这样做:
WeblogConfiguration.Current
简单直接
【讨论】:
【参考方案9】:If you are using environment variables as your configuration,您可以直接访问环境变量,而不是通过配置对象。
using System;
namespace My.Example
public static class GetPaths
private static readonly string MyPATH =
Environment.GetEnvironmentVariable("PATH");
private static readonly string MySpecialPath =
Environment.GetEnvironmentVariable("PREFIX_SpecialPath");
...
【讨论】:
【参考方案10】:我认为你可以使用扩展功能,像这样
public static string ConfigToSomeThing(this IConfiguration config, int value)
return config[value.ToString()] ?? "";
然后任何地方,只要注入IConfiguration并使用扩展方法
_systemConfiguration.ConfigToSomeThing(123);
【讨论】:
【参考方案11】:我刚刚创建了下面的类:
/// <summary>
///
/// </summary>
public static class ConfigurationManager
/// <summary>
///
/// </summary>
public sealed class ConfigurationManagerAppSettings
/// <summary>
///
/// </summary>
internal ConfigurationManagerAppSettings()
/// <summary>
///
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public string this[string key] => (TheConfiguration ?? throw new Exception("Set ConfigurationManager.TheConfiguration in Startup.cs")).GetSection($"AppSettings:key").Value;
/// <summary>
///
/// </summary>
public static IConfiguration? TheConfiguration get; set;
/// <summary>
///
/// </summary>
public static readonly ConfigurationManagerAppSettings AppSettings = new ConfigurationManagerAppSettings();
及以下代码:
public class Startup
public Startup(IConfiguration configuration)
Configuration = configuration;
public IConfiguration Configuration get;
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
ConfigurationManager.TheConfiguration = Configuration;
【讨论】:
【参考方案12】:这是一种从 NET.Core 页面获取配置值的方法,无需静态引用这些值,但仍然能够将它们传递给从非静态类调用的其他静态函数。
在你的非静态类的顶部添加这个:
private readonly IConfiguration _configuration;
然后在构造函数中引入现有配置作为函数的输入:
IConfiguration configuration
然后将配置分配给构造函数中的只读变量:
_configuration = configuration;
这是一个应该是什么样子的示例:
public class IndexModel : PageModel
private readonly IConfiguration _configuration;
public IndexModel(IConfiguration configuration)
_configuration = configuration;
在此之后,您可以通过引用 _configuration 来引用类中任何函数中的配置,甚至可以将其传递给您从其他类调用的其他静态函数:
public async Task OnGetAsync()
AnotherClass.SomeFunction(_configuration);
然后在被调用的静态类中我可以使用配置值:
public static string SomeFunction(IConfiguration configuration)
string SomeValue = configuration.GetSection("SomeSectionOfConfig")["SomeValue"];
我有一个类调用一些存储过程来查看和修改数据,并使用这种方法从 appsettings.json 传递参数值。
【讨论】:
问题是关于如何“从静态类访问配置”,所以我认为在这里告诉我们如何“无需静态引用这些配置”来获取配置并不是很有用【参考方案13】:考虑使用说明 here 进行 ASP.NET Core 配置。
您可以创建一个类来存储您的配置设置,然后访问这些值,如下所示:
_config.UserName
在启动 - ConfigureServices:
services.Configure<Config>(Configuration.GetSections("General"));
然后只需将您的对象注入您需要的任何位置:
IOptions<Config> config
【讨论】:
你打算如何在静态类中注入对象? 您将配置注入实例而不是使用静态类。我没有建议尝试将配置注入静态。 这个问题专门关于在static class
中使用它
@SerjSagan 尽管这个问题专门询问静态类,但他们可能不明白从 .NET 到 .NET Core 的范式转变。因此,静态类不是最佳答案,因此选项模式应该是有效答案。【参考方案14】:
IConfiguration 可在项目中的任何位置注入。但在静态类的情况下,我正在使用的选项可能只是方法......
var Configuration = new ConfigurationBuilder()
.AddUserSecrets<Startup>()
.Build();
而且,您可以添加所需的部分,例如在上面的代码块中,我添加了“UserSecrets”。
【讨论】:
你不能 DI 变成一个静态的,所以这是错误的答案。 嗨,billb,这就是我上面提到的。我们不能 DI 进入静态类,这就是上面代码块的原因。没有 DI 发生但允许访问配置,例如代码块中的“UserSecrets” 好吧,你不能 DI 进入一个静态类,但是你可以 DI 进入一个包含静态私有成员的类。添加到启动服务可能是最好的方法,但是它将您的项目耦合在一起并且使重用变得更加困难。我倾向于在我的服务类中包含静态,它们可以被完全封装。以上是关于ASP.NET Core——从静态类访问配置的主要内容,如果未能解决你的问题,请参考以下文章
来自静态类的 ASP.NET Core Web API 日志记录
一个文件搞定Asp.net core 3.1动态页面转静态页面
ASP.NET Core - 从 WebApi 提供静态内容 [重复]