I'm writing 2 WPF .Net Framework applications and have various protos, clients and implementations ready to use.
The problem:
App 1 sends first request (startup routine for App 2).
App 2 receives, handles, creates object to return, returns it.
App 3 throws Grpc.Core.RpcException: {"Status(StatusCode=Cancelled, Detail=\"Cancelled\")"}
I got the desired output, didn't set a timeout, and gave no cancellation token to even be called.
My channel is setup as follows, with 127.0.0.1 and 50052 as IP and Port.
_channel = new Channel($"{MyIP}:{MyPort}, ChannelCredentials.Insecure);
My server is setup by new'ing it up, iteratively adding services, then adding a port as:
_server.Ports.Add(new ServerPort({MyIP}, {MyPort},
ServerCredentials.Insecure));
protos were compiled using grpc's auto compile methods, with `syntax="proto3". I'll add a snippet below.
service Hardware
{
// A Simple RPC.
//
// Request for the hardware to initialise.
//
// Returns the state in an enum.
rpc Initialise(StateChangeRequest) returns (State) {}
}
// Requests the change, with context coming from the command being sent.
message StateChangeRequest
{
}
// Returns the state enum, showing the different states of the hardware.
message State
{
enum Status {
UNKNOWN = 0;
INITIALISED = 1;
INITFAILED = 2;
}
Status status = 1;
}
The call is made as follows:
public async Task<bool> Initialise()
{
try
{
// Initialise hardware via client.
if (await _client.InitialiseAsync() != State.Types.Status.Initialised)
{
return false;
}
}
catch (Exception ex)
{
_logger.Error(ex);
return false;
}
return true;
}
Any further information I can think of that may help:
Target Framework is .NET Framework 4.6.2
The Google Protobuf version I'm using is 3.9.1.0
The Grpc Core version I'm using is 2.0.0.0
The Grpc Core Api version I'm using is 2.0.0.0
These versions are identical in all projects used.
Any help is greatly appreciated.
Related
I'm using IdentityModel.OidcClient's SystemBrowser to request a token in a console app via browser. What I don't understand is, why there is an await Task.Delay(500) in the Dispose method of the LoopbackHttpListener. I see why you would do the Dispose via Task.Run, but not the delay.
Line of code in GitHub: https://github.com/IdentityModel/IdentityModel.OidcClient/blob/46d7b25cd71eb0be5f3c203c2525b1f357e408db/clients/ConsoleClientWithBrowser/SystemBrowser.cs#L131
public LoopbackHttpListener(int port, string path = null)
{
path = path ?? String.Empty;
if (path.StartsWith("/")) path = path.Substring(1);
_url = $"http://127.0.0.1:{port}/{path}";
_host = new WebHostBuilder()
.UseKestrel()
.UseUrls(_url)
.Configure(Configure)
.Build();
_host.Start();
}
public void Dispose()
{
Task.Run(async () =>
{
await Task.Delay(500);
_host.Dispose();
});
}
I don't know why that code is used but I can guess. This could only be answered by the maintainers, assuming they still remember why this was written after 6 years.
Clicking on Blame shows that this specific line is 6 years old, tagged as hacky dispose of Kestrel. Recent commits have messages like Update test client or Update sample client. The csproj file targets .NET 6 directly. If this was library code it would target .NET Standard 2.1 or .NET Core 3.1 as well, which are still supported.
My guess is that this code is used to ensure any pending requests handled by the listener complete before it closes. Since this started as test code, some hacks were used and never fixed, because they didn't directly impact the test's or sample's core behavior.
I don't remember what WebHost or the Generic Host looked back in 2017 (and I'm too lazy to find out). The interfaces have changed a lot between .NET Standard 2.0, .NET Core 3.1 and .NET 6.
In .NET 6 though we can use IAsyncDisposable to allow asynchronous disposal. Inside DisposeAsync we can call WebHost.StopAsync to gracefully shut down the host before disposing it:
public class LoopbackHttpListener : IAsyncDisposable
{
...
public async ValueTask DisposeAsync()
{
await _host.StopAsync(TimeSpan.FromMilliseconds(500));
_host.Dispose();
}
It's possible the LoopbackHttpListener can be replaced by Minimal APIs too.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", (HttpRequest request) =>{
_source.TrySetResult(request.QueryString);
return "<h1>You can now return to the application.</h1>";
});
_task=app.RunAsync(_url);
The test code returns a fixed response on any HTTP GET call and exposes the request's query string through the _source TaskCompletionSource.
Im creating API in .NET Core, which consumes WCF Service. Accessing WCF service is realised, by calling any method, getting an exception (Access denied), and than calling LogIn method using cookie returned in header with first call response. Than, after login i want to retry my original call. All exceptions are the same, and only message string is different. Here is my code for one method call:
public async Task<List<scheduleElement>> getSchedule(DateTime start, DateTime end)
{
bool secondTry = false;
while (true)
{
try
{
var data = await _scheduleServiceClient.getScheduleAsync(start, end);
if (data.#return == null) return new List<scheduleElement>();
return data.#return.ToList();
}
catch (Exception e)
{
if (!secondTry && e.Message.StartsWith("Access denied for WebService method:"))
{
var logged = await LogIntoSOAPServices();
if (!logged) throw;
}
else throw;
secondTry = true;
}
}
}
Im using proxies generated with WCF Web Service Reference Provider
This works, but Im looking for a way to globaly handle exceptions and retry logic like this, because im going to have to copy and paste tons of code. I have Exception handler in my API but if i catch this exceptions with it im not able to retry method i originaly called.
A common library for cases like these is Polly;
https://github.com/App-vNext/Polly
Its part of the dotnet foundation i believe and is quite commonly used.
You can handle specific exceptions or results and act on that, e.g.
// Retry once
Policy
.Handle<SomeExceptionType>()
.Retry()
The logic can get quite complex. For webApi's i usually follow this guide from msdn:
https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/implement-http-call-retries-exponential-backoff-polly
As .NET Remoting has been removed from .NET Core framework, I tried to use NetTcpBinding from the WCF library, but it's not included in .NET Core.
Is there some other analog of TCPChannel that I can use?
I would try to adopt a different RPC framework instead - ideally a platform-neutral one instead of one which is tightly coupled to .NET.
There are lots of options available. Just off the top of my head:
You could implement a Web API using ASP.NET Core, probably (but not necessarily) with a JSON payload.
You could use gRPC, probably (but not necessarily) using Protocol Buffers as the payload
You could use Thrift
Those are just examples - there are an awful lot of RPC and RPC-like frameworks available. None of these will be as "transparent" as using remoting, but:
They'll make it a lot clearer when you're making a network call
They'll allow you to evolve between service versions more easily
They'll allow you to use a mixture of platforms for servers and clients - which you may not need right now, but is good for future-proofing
If you have a large codebase that is based on .NET Remoting, then switching to WebAPI or gRPC could lead to rewrite half of your application.
CoreRemoting (MIT licensed) may be an alternative: https://github.com/theRainbird/CoreRemoting
It makes is possible to migrate .NET Remoting based Client/Server applications to .NET Core / .NET 5.
In contrast to gRPC or WebAPI, the procedure for CoreRemoting is very similar to .NET Remoting. Only remote method calls are made between .NET objects. A conversion of the calls to HTTP calls (building URLs with string concatenation) as with WebAPI is not necessary. Interfaces between client and server are defined in shared .NET assemblies instead of in a special interface language as with gRPC. Events and callbacks are supported out-of-the-box and can be used in a natural way for a C# developer (Compared to gRPC's more complex streaming approach).
The following example shows how a simple client/server chat application can be created using CoreRemoting.
Shared Contract Assembly
namespace HelloWorld.Shared
{
public interface ISayHelloService
{
event Action<string, string> MessageReceived;
void Say(string name, string message);
}
}
Server
using System;
using CoreRemoting;
using CoreRemoting.DependencyInjection;
using HelloWorld.Shared;
namespace HelloWorld.Server
{
public class SayHelloService : ISayHelloService
{
// Event to notify clients when users post new chat messages
public event Action<string, string> MessageReceived;
// Call via RPC to say something in the chat
public void Say(string name, string message)
{
MessageReceived?.Invoke(name, message);
}
}
public static class HelloWorldServer
{
static void Main(string[] args)
{
using var server = new RemotingServer(new ServerConfig()
{
HostName = "localhost",
NetworkPort = 9090,
RegisterServicesAction = container =>
{
// Make SayHelloSevice class available for RPC calls from clients
container.RegisterService<ISayHelloService, SayHelloService>(ServiceLifetime.Singleton);
}
});
server.Start();
Console.WriteLine("Server is running.");
Console.ReadLine();
}
}
}
Client
using System;
using CoreRemoting;
using HelloWorld.Shared;
namespace HelloWorld.Client
{
public static class HelloWorldClient
{
static void Main(string[] args)
{
using var client = new RemotingClient(new ClientConfig()
{
ServerHostName = "localhost",
ServerPort = 9090
});
client.Connect();
// Create a proxy of the remote service, which behaves almost like a regular local object
var proxy = client.CreateProxy<ISayHelloService>();
// Receive chat messages send by other remote users by event
proxy.MessageReceived += (senderName, message) =>
Console.WriteLine($"\n {senderName} says: {message}\n");
Console.WriteLine("What's your name?");
var name = Console.ReadLine();
Console.WriteLine("\nEntered chat. Type 'quit' to leave.");
bool quit = false;
while (!quit)
{
var text = Console.ReadLine();
if (text != null && text.Equals("quit", StringComparison.InvariantCultureIgnoreCase))
quit = true;
else
{
// Post a new chat message
proxy.Say(name, text);
}
}
}
}
}
CoreRemoting is only working from .NET to .NET. If you need to communicate with Javascript, Java, Python, ..., then it is not the right tool.
But if you only want to do RPC in a pure .NET environment and you want to do it in a comfortable way, thean CoreRemoting may be very helpful.
I would like to note that I am the developer of the CoreRemoting project.
We have a requirement to provide an API endpoint which reports the health of various external dependencies. One of these is an Azure Service Bus. By health we simply need to know if the service is available and responding to connections.
Our application already starts up a service bus endpoint on startup and uses this to publish messages to its queue. However, it looks like the only way I can test this endpoint's health would be to actually publish a message to the queue and check for errors. I'd rather not do this because having to clean up these message later feels like overkill.
My other idea was to use a dedicated class to create a new endpoint and start it. Then stop it again if there are no errors, as below. And do this each time I need to check the health.
// Build the service bus configuration - connection string etc.
var configuration = _configurationBuilder.Configure(_settings);
IEndpointInstance serviceBusEndpoint = null;
try
{
serviceBusEndpoint = await Endpoint.Start(configuration);
return true;
}
catch
{
return false;
}
finally
{
if (serviceBusEndpoint != null)
{
await serviceBusEndpoint.Stop();
}
}
However, I suspect this may be a less efficient approach. Is there a better/correct way to achieve this aim?
I always knew SignalR as fitting perfectly with browser based applications in real time.
In general to pushing server side processing messages to the client, that is a "listening" web page.
It is possible to do the same with client that is not a web application, but a desktop application, pushing it in real-time?
In short, yes. The samples on github show you how, for instance the console client sample, and the documentation in the wiki shows you how you can build a .NET client. To quote that documentation (warning, version dependent, it works right now, but may be different in the future):
var hubConnection = new HubConnection("http://www.contoso.com/");
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
stockTickerHub.On("notify", () =>
// Context is a reference to SynchronizationContext.Current
Context.Post(delegate
{
textBox.Text += "Notified!\n";
}, null)
);
await hubConnection.Start();
// or do the following for a synchronous method:
// connection.Start().Wait();
See ASP.NET: ASP.NET SignalR Hubs API Guide for the above code.
I have made a wrapper around the .NET client that makes it really easy to implement listeners on the local client
https://github.com/AndersMalmgren/SignalR.EventAggregatorProxy/wiki/.NET-Client
Once set up you just add a listener like
public class MyViewModel : IHandle<MyEvent>
{
public MyViewModel(IEventAggregator eventAggregator)
{
eventAggregator.Subscribe(this);
}
public void Handle(MyEvent message)
{
//Act on MyEvent
}
}