我目前正在 .net 核心中制作(至少尝试)一个简单的控制台应用程序。该应用程序是一个简单的控制台应用程序。没什么特别的,RabbitMQ的一个小实验。
.net 版本(今天早上刚更新到最新最好的):
dotnet --version
5.0.103
操作系统(不确定是否有用):
macOS Big Sur 11.2.1
当我从 IDE(在本例中为 Rider)运行应用程序时,没问题。在这种情况下,一切都符合预期。
由于我想从终端运行应用程序,因此发生了以下情况:应用程序无法启动,因为它无法创建到本地主机的连接。
我使用的命令
dotnet run --project src/RabbitMQ.Publisher/RabbitMQ.Publisher.csproj
...
Unhandled exception. RabbitMQ.Client.Exceptions.BrokerUnreachableException: None of the specified endpoints were reachable
---> System.AggregateException: One or more errors occurred. (Connection failed)
---> RabbitMQ.Client.Exceptions.ConnectFailureException: Connection failed
---> System.ArgumentNullException: Value cannot be null. (Parameter 'hostName')
...
阅读了一些关于 .NET Generic Host 和 dotnet run 的文档,我找不到任何关于为什么会失败的特殊原因。在过去,只有在我忘记将 appsettings.json
包含到项目中时才会发生这种情况(以我目前的经验)。我检查的第一件事。
<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
接下来我检查的是 Host.CreateDefaultBuilder
方法的实现,因为我认为发生了一些变化。一切似乎都很好并且向后兼容。
源代码:
public static void Main(string[] args)
{
using IHost host = CreateBuilder(args).Build();
using Publisher? publisher = host.Services.GetService<Publisher>();
Debug.Assert(publisher != null, nameof(publisher) + " != null");
publisher.BasicPublish(new BasicPayload
{
Message = "Test message"
});
}
private static IHostBuilder CreateBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.Configure<RabbitMQ>(hostContext.Configuration.GetSection("RabbitMQ").Bind);
services.AddTransient<IConnectionFactory>(provider =>
{
IOptions<RabbitMQ>? options = provider.GetService<IOptions<RabbitMQ>>();
Debug.Assert(options != null, nameof(options) + " != null");
return new ConnectionFactory
{
HostName = options.Value.HostName
};
});
services.AddTransient<Publisher>();
});
选项类:
using System.ComponentModel.DataAnnotations;
public sealed class RabbitMQ
{
[Required] public string HostName { get; set; } = default!;
[Required] public string Queue { get; set; } = default!;
[Required] public bool Durable { get; set; }
}
appsettings.json
的内容:
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"RabbitMQ": {
"HostName": "localhost",
"Queue": "basic",
"Durable": false
}
}
其余文件的内容并不重要。
我还在 SO、不同的博客文章等上查看了一些其他类似的问题。我想我错过了一些东西,但出于对应用程序配置的热爱,我似乎无法找到它。还有什么我想到的,我可能会错过的吗?
由于这是一个实验性项目,我的 CDO 在弄清楚发生了什么之前不允许我继续:)
编辑:
最新实验:
.ConfigureAppConfiguration(builder =>
{
string path = Assembly.GetExecutingAssembly().Location;
string? directoryName = Path.GetDirectoryName(path);
builder.AddJsonFile($"{directoryName}/appsettings.json", optional: false);
})
似乎“修复”了这个问题。所以,现在我正在研究实际复制到输出目录的内容。 CreateDefaultBuilder
是“搜索”appsettings.json
时的默认选项是optional
。我假设这会失败(我可以重写应用程序并手动配置提供程序来证明这个假设,但不值得花时间),如果将其设置为 true
。
会及时通知您调查结果:)
编辑#2:
在检查 CreateDefaultBuilder
的代码后,一切开始变得有意义。结合 dotnet run --project ./projects/proj1/proj1.csproj
(来自 dotnet run 文档页面的示例),它当然会失败。因为它将“搜索”文件的路径基于 Directory.GetCurrentDirectory()
。我从存储库的根目录运行命令,那么自然不会包含 appsettings.json
文件。项目结构:
.
├── Example.RabbitMQ.sln
├── README.md
├── docker-compose.yaml
├── src
│ └── RabbitMQ.Publisher
│ ├── Application
│ ├── Models
│ ├── Options
│ ├── Program.cs
│ ├── Properties
│ ├── RabbitMQ.Publisher.csproj
│ ├── appsettings.json
│ ├── bin
│ └── obj
└── test
为了给后代说明这一点:)
当我以下列方式运行命令时:dotnet run --project src/RabbitMQ.Publisher/RabbitMQ.Publisher.csproj
那么它将“搜索”应用设置的路径基于该 src
目录。其中,根据项目结构,手头没有文件:)
感谢橡皮鸭们,这是一个有趣的实验。现在回到最初的实验开始。