C# 在.Net Core 2.2类库项目中未读取/检测到App.config值
我正在.net core 2.2中创建类库项目,该项目可用于.net core和.net framework 因为它是类库项目,所以我添加了app.config来读取配置值。已从Nuget软件包安装ConfigurationManager。下面是app.config文件C# 在.Net Core 2.2类库项目中未读取/检测到App.config值,c#,asp.net-core,.net-core,.net-core-2.2,C#,Asp.net Core,.net Core,.net Core 2.2,我正在.net core 2.2中创建类库项目,该项目可用于.net core和.net framework 因为它是类库项目,所以我添加了app.config来读取配置值。已从Nuget软件包安装ConfigurationManager。下面是app.config文件 <?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="InputKe
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="InputKey" value="12345" />
</appSettings>
</configuration>
添加了Nunit测试项目,并在其中添加了app.config。该项目已成功构建。当我调试我的测试用例时,下面的行总是返回null
Install-Package Microsoft.Extensions.Options
Install-Package Microsoft.Extensions.DependencyInjection
在代码中
public static void Main()
{
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
}
private static void ConfigureServices(IServiceCollection serviceCollection)
{
var currentDir = ProjectPath.GetApplicationRoot();
// build configuration
varconfiguration = newConfigurationBuilder()
.SetBasePath(currentDir)
.AddJsonFile("appsettings.json",false)
.Build();
serviceCollection.AddOptions();
serviceCollection.Configure<WebJobSettings>(configuration.GetSection("WebJobSettings"));
// add app
serviceCollection.AddTransient<WebJob>();
}
publicstaticvoidmain()
{
var servicecolection=新servicecolection();
配置服务(serviceCollection);
}
专用静态void配置服务(IServiceCollection serviceCollection)
{
var currentDir=ProjectPath.GetApplicationRoot();
//构建配置
varconfiguration=newConfigurationBuilder()
.SetBasePath(当前目录)
.AddJsonFile(“appsettings.json”,false)
.Build();
serviceCollection.AddOptions();
Configure(configuration.GetSection(“WebJobSettings”);
//添加应用程序
serviceCollection.AddTransient();
}
那就这样读吧
public class WebJob
{
private readonly IOptions<WebJobSettings> _webJobSettings;
public WebJob(
IOptions<WebJobSettings> webJobSettings)
{
_webJobSettings = webJobSettings;
}
}
公共类WebJob
{
私有只读IOPS\u webJobSettings;
公共网络作业(
IOptions(网络作业设置)
{
_webJobSettings=webJobSettings;
}
}
首先不要让配置文件进入单元测试!改为使用模拟来传递配置值。这有几个优点:
- 您将能够测试不同的配置值集,而不仅仅是当前配置文件中的配置值集
- 您将能够针对无效输入测试配置值
- 您不必记住始终维护配置文件的另一个工作副本
- 更干净的体系结构:一般来说,单元测试不应该接触物理文件,而应该只测试类的行为
// The class that is accessed in order to get some config setting
public class AppConfig
{
// The provider reads from a physical file when your lib is shipped but in your tests it will be mocked
private readonly AppConfigProvider provider;
// Constructor used by your app or library
public AppConfig() : this(new AppConfigProvider())
{
}
// Constructor that can be used by your unit testing framework to inject a mock
internal AppConfig(AppConfigProvider provider)
{
this.provider = provider;
}
public string InputKey => GetConfigValueOrThrow("InputKey");
private string GetConfigValueOrThrow(string configKey)
{
string value = provider.GetSetting(configKey);
if (value == null)
throw new Exception($"A value for key {configKey} is missing in the App.config file.");
return value;
}
}
// Provider implementation that actually reads from the app.config file
internal class AppConfigProvider
{
internal virtual string GetSetting(string configKey)
{
return ConfigurationManager.AppSettings[configKey];
}
}
// In the test class the config values will be returned by a mock instead of a physical file. This example uses Moq as mocking framework.
public class AppConfigTest
{
private Mock<AppConfigProvider> mock;
private AppConfig appConfig;
[SetUp]
public void Setup()
{
mock = new Mock<AppConfigProvider>();
appConfig = new AppConfig(mock.Object);
}
[Test]
public void TestInputKeyWhenValueIsValid()
{
mock.Setup(provider => provider.GetSetting("InputKey")).Returns("12345");
Assert.AreEqual("12345", appConfig.InputKey);
}
}
//为获取某些配置设置而访问的类
公共类AppConfig
{
//提供程序在lib发出时读取物理文件,但在测试中它将被模拟
私有只读AppConfigProvider;
//应用程序或库使用的构造函数
公共AppConfig():此(新AppConfigProvider())
{
}
//可由单元测试框架用于注入模拟的构造函数
内部AppConfig(AppConfigProvider提供程序)
{
this.provider=提供者;
}
公共字符串InputKey=>GetConfigValueOrThrow(“InputKey”);
私有字符串GetConfigValueOrThrow(字符串configKey)
{
字符串值=provider.GetSetting(configKey);
如果(值==null)
抛出新异常($“App.config文件中缺少键{configKey}的值。”);
返回值;
}
}
//实际读取app.config文件的提供程序实现
内部类AppConfigProvider
{
内部虚拟字符串GetSetting(字符串配置键)
{
返回ConfigurationManager.AppSettings[configKey];
}
}
//在测试类中,配置值将由模拟而不是物理文件返回。本例使用Moq作为模拟框架。
公共类AppConfigTest
{
私人模拟;
私有AppConfig-AppConfig;
[设置]
公共作废设置()
{
mock=新mock();
appConfig=newappconfig(mock.Object);
}
[测试]
public void TestInputKeyWhenValueIsValid()
{
mock.Setup(provider=>provider.GetSetting(“InputKey”))。返回(“12345”);
AreEqual(“12345”,appConfig.InputKey);
}
}
在.csproj文件中添加上述代码解决了我的问题
由于我的要求是在.net framework和.net core中同时使用dll,所以我必须使用.net标准而不是.netcore或.netframework创建项目
感谢@Panagiotis Kanavos的建议。这不是来自net standard 2+的“appsettings.json”吗?如果我使用appsettings.json,我可以将此类库用于.net framework项目吗?我已经在Nunit测试项目的.csproj中完成了上述工作。如果其他一些.net framework应用程序想要使用这个类库,他们是否也需要在那里添加上述代码?我的偏好是,他们只需引用dll并使用它。@Govind所有Microsoft.Extensions.*软件包都是.NET标准2.0,因此您可以在任何地方使用它们。您不需要使用
appsettings.json
无论如何,Configuration
包可以与多个提供者一起工作。事实上,您可能应该使用config类和Secrets特性来保护输入密钥development@Govind如果您想在两个运行时都使用项目而不出现问题或警告,那么它必须是.NET标准的。这与配置无关。NET核心2.2。具有.NET Framework中不存在的API。当您从.NET Framework项目引用.NET核心程序集时,您的编译器应该已经在抱怨了,我可以这么做。但我的要求不是这样。我只想复制其他第三方消费者如何使用我的dll进行访问。因为我想在.net framework、.net core和其他平台上使用它,所以它应该与较低版本兼容。我的问题不是如何对app.config进行模拟。我倒是问了
// The class that is accessed in order to get some config setting
public class AppConfig
{
// The provider reads from a physical file when your lib is shipped but in your tests it will be mocked
private readonly AppConfigProvider provider;
// Constructor used by your app or library
public AppConfig() : this(new AppConfigProvider())
{
}
// Constructor that can be used by your unit testing framework to inject a mock
internal AppConfig(AppConfigProvider provider)
{
this.provider = provider;
}
public string InputKey => GetConfigValueOrThrow("InputKey");
private string GetConfigValueOrThrow(string configKey)
{
string value = provider.GetSetting(configKey);
if (value == null)
throw new Exception($"A value for key {configKey} is missing in the App.config file.");
return value;
}
}
// Provider implementation that actually reads from the app.config file
internal class AppConfigProvider
{
internal virtual string GetSetting(string configKey)
{
return ConfigurationManager.AppSettings[configKey];
}
}
// In the test class the config values will be returned by a mock instead of a physical file. This example uses Moq as mocking framework.
public class AppConfigTest
{
private Mock<AppConfigProvider> mock;
private AppConfig appConfig;
[SetUp]
public void Setup()
{
mock = new Mock<AppConfigProvider>();
appConfig = new AppConfig(mock.Object);
}
[Test]
public void TestInputKeyWhenValueIsValid()
{
mock.Setup(provider => provider.GetSetting("InputKey")).Returns("12345");
Assert.AreEqual("12345", appConfig.InputKey);
}
}
<Target Name="CopyCustomContent" AfterTargets="AfterBuild">
<Copy SourceFiles="app.config" DestinationFiles="$(OutDir)\testhost.dll.config" />
</Target>