cyanfish / grpc-dotnet-namedpipes

Named pipe transport for gRPC in C#/.NET
Apache License 2.0
190 stars 48 forks source link

Dependency on Google.Protobuf #23

Closed DSanchen closed 3 years ago

DSanchen commented 3 years ago

Just came across your package for Named Pipes for gRPC, which is exactly what I was looking for (I'm looking for alternatives for WCF with namedpipebindings...) Unfortunately using the package as is did not work for me "out of the box", I somewhere got an "the Pipe is broken" exception. Digging further in, I realized that the pipe worked fine, rather it was the fact that you "need" Google.Protobuf 3.14, but I use another Package (Akka.Streams) that itself needs Google.Protobuf >= 3.15.6

Would it be possible, to specify a dependency of Google.Protobuf 3.* ? I tried compiling it like that and it worked for me...

cyanfish commented 3 years ago

It should work with any Google.Protobuf version >= 3.14. Can you try the latest GrpcDotNetNamedPipes version I published (1.4.0)? There's a bug fix that might be relevant.

DSanchen commented 3 years ago

Hi cyanfish, I tried it with your NuGet Package V1.40 but still have no success. I've added a callback to AppDomain.CurrentDomain.AssemblyResolve. When the client tries to connect to the server, the assemblyResolve callback is called, specifying "Google.Protobuf, Version=3.14.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604" as the assembly to resolve...

When I compile your source code as package reference, there is still the fixed dependency to Google.Protobuf 3.14.0 in the csproj-file. Then, when the client tries to call the first service-call I can see an exception happening in PipeReader.ReadLoop() that is caught:

ExceptionWhileReading

This seems like Package Hell to me, that cannot fix when using NuGet Packages.... When I self-compile your code with it works.

cyanfish commented 3 years ago

I can't reproduce the issue. As you can see on the nuget package, the dependencies are defined correctly.

Sounds like an issue with your Nuget configuration but I don't know exactly. Adding a binding redirect may work.

DSanchen commented 3 years ago

Hi cyanfish, Thanks for your answer and your time. You're right, it has something to do with binding redirection, I just don't know how to fix it (properly). The point is, that I'm loading the assembly which starts the GRPC server dynamically at runtime (using Castle Windsor). The started application itself does not know (and should not know) anything about gRPC or Protobuf. Then, when the server is started, everything seems fine, until the client connects to the server. At this point, your PipeReader class gets created, and the ReadLoop starts. At the point, where the ReadLoop calls NamedPipeTransport.Read(), the TransportMessage Type is constructed for the first time. This is the moment, when the application domain think it has to load Google.Protobuf Version 3.14.0.0 which it can't because 3.15.7 is loaded... Of course I can include the into the app.config of my main app, but this feels awkward, because the main app (as stated) does not know anything about gRPC or Protobuf... If you're interested, I could send you my code to reproduce the issue. But as I said, I think it has nothing to do with your code, I'm just curious how this could be fixed...

DSanchen commented 3 years ago

One way to solve it (quite nicely), is to provide the already loaded protobuf-assembly on the AssemblyResolve Event:

private static Assembly AssemblyResolve_With_LoadedAssembly(object sender, ResolveEventArgs args)
{
    Func<AssemblyName, AssemblyName, bool> isReplacementFor = (aName, bName) => aName.Name.Equals(bName.Name) && aName.GetPublicKeyToken().SequenceEqual(bName.GetPublicKeyToken());
    try
    {
        Assembly replacementAssembly = AppDomain.CurrentDomain.GetAssemblies().Where(assy => isReplacementFor(assy.GetName(), new AssemblyName(args.Name))).Single();
        return replacementAssembly;
    }
    catch
    {
         return null;
    }
}