WPF .NET Core中的gRPC错误

8

我想创建一个简单的WPF Core,gRPC项目。这个"代码"在我的.NET Core Console应用中完美运行,但是在WPF中似乎有些特别。

Proto文件

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控制台应用程序->将Server中的Proto文件添加到Client中,并将gRPC存根类更改为仅客户端

使用:.NET Core 3.1

具有以下NuGets:

  • 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 Core

创建默认的 WPF .NET Core 应用程序 -> 从服务器添加 Proto 文件到客户端并将 gRPC Stub 类 更改为 仅客户端

使用: .NET Core 3.1

具有以下 NuGets:

  • 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应用程序

错误:

'MyProtoNamespace'类型或命名空间在'MyWPFNameSpace'命名空间中不存在(是否缺少编译器引用?)


你在哪里定义了.proto文件?控制台应用程序项目文件与WPF应用程序项目文件之间有什么区别? - mm8
@mm8 我在每个项目中都定义了我的 proto 文件(例如,WPF 有自己的 proto,控制台也有自己的)。关于你的第二个问题:没有什么区别,它们都只是客户端,我的 grpc 服务器 proto 也具有相同的内容,但它只是服务器端。 - user12722843
2个回答

9
这个问题与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" });
};

谢谢!我整个早上都被这个问题困扰着。 - undefined

3

在WPF项目文件中添加这一行,文件扩展名为“*.csproj”,具体位置是在标签<PropertyGroup>中,然后重新构建:

<CoreCompileDependsOn>$(CoreCompileDependsOn);Protobuf_Compile</CoreCompileDependsOn>

在.csproj文件中,它将如下所示:
"..."
    <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>net6.0-windows</TargetFramework>
        <Nullable>enable</Nullable>
        <UseWPF>true</UseWPF>
        <CoreCompileDependsOn>$(CoreCompileDependsOn);Protobuf_Compile</CoreCompileDependsOn>
      </PropertyGroup>
"..."

更详细的内容请参考视频:https://youtu.be/HFF2gstZS1A?t=106 - Luiz
在我的Net6.0 WPF项目中,它运行良好。 - Kun Ma
这个能用于发布应用程序吗?当我将它添加到发布配置文件时,构建失败并显示以下错误信息:“项目中不存在目标“Protobuf_Compile”。” - Michael Gulik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接