c# - WPF .NET Core 中的 gRPC 错误
问题描述
我想创建一个简单的 WPF Core、gRPC 项目。此“代码”在我的 .NET Core 控制台应用程序中完美运行,但 WPF 似乎有些特别。
原始文件
syntax = "proto3";
option csharp_namespace = "MyProtoNamespace";
package greet;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply);
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings.
message HelloReply {
string message = 1;
}
gRPC 服务器
在 Visual Studio 2019 中创建了一个默认模板(使用 .NET Core 3.1)
控制台应用程序(完美运行)
创建了一个默认的 .NET Core 控制台应用程序 -> 将 Proto 文件从服务器添加到客户端,并将gRPC 存根类更改为仅客户端
用途:.NET Core 3.1
有以下 NuGet:
- Grpc.Tools
- Grpc.Net.Client
- Google.Protobuf
代码
static async Task Main(string[] args) // works perfectly
{
using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greeter.GreeterClient(channel);
var reply = await client.SayHelloAsync(new HelloRequest { Name = "GreeterClient" });
System.Console.WriteLine("Greeting: " + reply.Message);
System.Console.WriteLine("Press any key to exit...");
System.Console.ReadKey();
}
WPF .NET 核心
创建了一个默认的 WPF .NET Core 应用程序 -> 将 Proto 文件从服务器添加到客户端,并将gRPC 存根类更改为仅客户端
用途:.NET Core 3.1
有以下 NuGet:
- Grpc.Tools
- Grpc.Net.Client
- Google.Protobuf
代码
Loaded += async delegate
{
using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greeter.GreeterClient(channel);
var reply = await client.SayHelloAsync(new HelloRequest { Name = "GreeterClient" });
};
问题
我无法构建 WPF 应用程序
错误:
命名空间“MyWPFNameSpace”中不存在类型或命名空间名称“MyProtoNamespace”(您是否缺少程序集引用?)
解决方案
此问题与Grpc.Tools
WPF 应用程序的包和特殊构建过程有关。该Grpc.Tools
包负责编译 .proto 文件并生成服务类。
解决方案是将 gRPC 客户端实例化移动到单独的 .NET Core 类库程序集(项目)中。只需将此代码提取到一个类中,例如GrpcServiceProvider
在这个新程序集中。让我们GrpcServiceProvider
返回一个 gRPC 客户端的实例。现在从您的 WPF 程序集中引用 .NET Core 库程序集和GrpcServiceProvider
类以获取客户端的实例。
这将修复构建错误并改进应用程序设计。
不要忘记GrpcChannel
实现IDisposable
。这意味着GrpcServiceProvider
也应该实施它并正确处置其资源。
.NET Core 类库项目
public class GrpcServiceProvider : IDisposable
{
public GrpcServiceProvider()
{
this.ServiceUrl = "https://localhost:5001";
this.DefaultRpcChannel = new Lazy<GrpcChannel>(GrpcChannel.ForAddress(this.ServiceUrl));
}
public Greeter.GreeterClient GetGreeterClient() => this.GreeterClient ??= new Greeter.GreeterClient(this.DefaultRpcChannel.Value);
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
if (this.DefaultRpcChannel.IsValueCreated)
{
this.DefaultRpcChannel.Value.Dispose();
}
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
disposedValue = true;
}
}
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
// ~GrpcServiceProvider()
// {
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
// Dispose(false);
// }
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
// TODO: uncomment the following line if the finalizer is overridden above.
// GC.SuppressFinalize(this);
}
#endregion IDisposable Support
private Lazy<GrpcChannel> DefaultRpcChannel { get; set; }
private string ServiceUrl { get; set; }
private Greeter.GreeterClient GreeterClient { get; set; }
}
WPF项目
Loaded += async delegate
{
using var serviceProvider = new GrpcServiceProvider();
var client = serviceProvider.GetGreeterClient();
var reply = await client.SayHelloAsync(new HelloRequest { Name = "GreeterClient" });
};
推荐阅读
- signature - 亚马逊卖家中心 MWS ListOrders GET 请求失败,并显示“我们计算的请求签名与您提供的签名不匹配”。
- r - 在 R 中使用 nls 函数
- php - 如何解决 ssl3_get_server_certificate 错误
- r - 将一个 data.frame 映射到另一个
- java - 在 Dropwizard 中公开 Prometheus 指标
- powershell - 如何等待点源脚本完成?
- git - 无法使用来自 URL 的插件构建
- python - Python:根据日期绘制不同颜色的分散数据
- swift - 如何在 appDelegate 方法上设置 Timer 时间表?
- r - R Group by 用于对值进行分组和求和的函数给出了问题