Azure / azure-relay-dotnet

☁️ .NET Standard client library for Azure Relay Hybrid Connections

Home Page:https://docs.microsoft.com/en-us/azure/service-bus-relay/relay-what-is-it

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

WCF Relay on dotnet standard

gautelo opened this issue · comments

Is there support for connecting to Azure WCF Relay from a dotnet core application?

On the landing page it tells me "For Relay Hybrid Connections samples, see the azure/azure-relay service repository."

If I go up a level it tells me that "The WCF Relay features are only available for the full .NET Framework on Windows, which they depend on."

Does this mean that there is no support for connecting to a WCF service over azure relay from a dotnet core application and that I must use the full framework to be able to connect? I saw that the WCF client would be supported even though WCF hosting was cut, so I was hopeful that this would indeed be supported. However from this repo it seems only Hybrid Connections have netstandard support. Correct?

Our scenario includes hosting a WCF WebService on-prem for multiple customers. Our primary application which accesses these are sometimes hosted in a private cloud and sometimes on-prem. For this reason WCF has been very useful, since it's allowed us to connect in both of these scenarios with a small configuration change.

So now that we're pushing to move to dotnet core, this is a snag that I wonder how to overcome. If there is indeed no support for this scenario in netstandard, could you suggest some other technology that would solve this for me?

Thanks!

A WcfRelay Service can use an HTTP binding, such as WebHttpRelayBinding. This allows clients/senders to speak normal HTTP. So if your client/sender is .net core it could just use HttpClient or similar. The WcfRelay Listener, e.g. ServiceHost, wouldn't be able to run on .net core though.

@dlstucki Wouldn't that require the on prem WCF service to expose an unsecured endpoint though? I've seen some examples of code using HttpClient to use Soap through explicit coding to make this work. But that seems extremely cumbersome.

Also, if I were to do this, it would force my consumer (the application that is sometimes in the cloud and sometimes on prem) to always talk http with the WCF service that is always on prem. Then I lose the quicker capabilities of other bindings unless I branch my consumers code to sometimes use HttpClient and sometimes a ChannelFactory.

The problem with that is that all of this is sort of a framework used with different implementation for different flavour of customer, so it would cause a lot of overhead.

That's why I would really like to be able to use the ChannelFactory in both scenarios. It allows me to keep the same client code for both scenarios.

I was being simple when I said HTTP instead of HTTPS. All the WCF Relay bindings (*HttpRelayBinding) by default require transport security (HTTPS) and client auth. These can be turned off at the endpoint level if desired.

If the WCF Relay Listener uses BasicHttpRelayBinding then the sender can use BasicRelayBinding (built-in WCF). If the WCF Relay Listener uses WebHttpRelayBinding the sender could use WebHttpBinding (built-in WCF), HttpsTransportBindingElement+TextMessageEncodingBindingElement (if message is XML) or the sender could simply use whatever HTTP API worked for them and make the call like other REST calls as long as the Shared Access Signature Token was provided with the HTTPS request. Basically when a WCF Relay Listener uses any of the HttpRelayBindings(note the Relay in there) the protocol between the sender and the Relay cloud service at https//.servicebus.windows.net is the same as the built-in WCF HttpBindings. (In these modes only the WCF Relay Listener talks a special protocol with the relay cloud service).

If you use the built-in WCF bindings and the WCF team provides support for .net core for sending with the HttpTransport then I think you'll be able to achieve what you're asking for.

This sample may help:
https://github.com/Azure/azure-relay/tree/master/samples/wcf-relay/RelayHttp

I'm at the airport right now typing on my phone. If this isn't enough info for you let me know and I'll try to find or put together something more helpful within a few days.

Edit: I realized with the built-in Http bindings one will need to ensure the Authorization header gets added to the HTTP headers. I think I should be able to write a custom WCF endpoint behaviour to take the SAS key name and SAS key, generates a token and adds it to those headers.

commented

@dlstucki - We have a hybrid solution similar to @gautelo. Can you point to some sample on building .net core client for WCF Relay?

Thanks.

@rsaa - Can you share a bit more info? Do you have an existing, deployed WcfRelay solution which you cannot change? If so, which Relay Binding are you using and how is it configured?

As far as sending from a .NET Core client to a WcfRelay Listener on .NET Framework any of the WebHttpRelayBinding samples would allow this since WebHttpRelayBinding doesn't require anything other than HTTP support on the sender and the ability to generate an appropriate SharedAccessSignature token to put in the Authorization/ServiceBusAuthorization HTTP Header.

The only way to host an Azure Relay listener in a .NET Core application would be to use HybridConnection instead of WcfRelay.

commented

@dlstucki

Yes, we have an existing hybrid solution using netTcpRelay binding on both listener and sender sides. We will keep listener on .NET 4.x but would like to update client that's ASP.NET Web API app to ASP.NET Core.

Thanks.

commented

@dlstucki , for folks trying to migrate to .NET Core, inability to migrate WCF Relay as such to .NET Core is a pain point.

Let me explain with an example.
I need to migrate client , with shared interfaces with server to .NET Core... say, 100s of OperationContract.

   [ServiceContract]
    public interface IBackendApi
    {
        [OperationContract]
        TestObject1 TestMessage(string param1, TestClass2 param2, TestClass3 param3, int param4);
    }

In this, only client needs to be migrated to .NET Core (because it needs it has to be hosted in Azure Functions V2). But, server will still be hosted in .NET Framework, hence can use all functionalities of Relay.

The only option for us right now is, to use Hybrid Connectivity. If we want to use, Wcf Relay with BasicHttpRelayBinding, as an example you mentioned, we would have to rewrite the protocol for all of client (100s of them), also for server. It doesn't seem as easy as ChannelFactory.Create ...

Should we be writing independent parsing logic, for 100s of OperationContract in the interface?
var image = client.GetStreamAsync(this.sendAddress + "/image").GetAwaiter().GetResult();
Also, how can we send multiple parameters and get custom return objects, should we write

WebHttpRelayBinding not implemented in .NET Core.

That's the pain point - host in .NET Core + migrating 100s of existing OperationContracts. An example where we don't have to deal with this scenario.

Edit: tried combinations of BasicHttp, getting errors "System.ServiceModel.ProtocolException: 'The remote server returned an unexpected response: (400) Bad Request.'". A valid example would have greatly helped.

Here's an example of an Azure Relay WCF Listener being called from a .NET Core Sender application:

Common WCF Contract

[ServiceContract(Namespace = "http://samples.microsoft.com/Relay")]
public interface IBillingBackendApi
{
    [OperationContract]
    string TestMessage(string message);
}

.NET Framework WCF Relay Listener

Target Framework: .NET Framework 4.5.2
Add a Nuget Package Reference to WindowsAzure.ServiceBus (Microsoft.ServiceBus.dll)

string address = "https://your-relay.servicebus.windows.net/WcfCoreDemo";
var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider("WcfCoreDemoKey", "XXXX...=");
var binding = new BasicHttpRelayBinding(); // a Relay Binding

var host = new ServiceHost(typeof(BackendService));
var endpoint = host.AddServiceEndpoint(typeof(IBillingBackendApi), binding, address);
endpoint.EndpointBehaviors.Add(new TransportClientEndpointBehavior(tokenProvider));
host.Open();

.NET Core WCF Sender

Target Framework: .NET Core 2.1
Add a Nuget Package Reference to System.ServiceModel.Http
Add a Nuget Package Reference to Microsoft.Azure.Relay (for TokenProvider)

string address = "https://your-relay.servicebus.windows.net/WcfCoreDemo";
var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider("WcfCoreDemoKey", "XXXX...=");
var binding = new BasicHttpsBinding(); // This is a plain old WCF Binding not Relay

var channelFactory = new ChannelFactory<IBillingBackendApi>(binding, new EndpointAddress(address));
var billingClient = channelFactory.CreateChannel();

// Use OperationContext to add the ServiceBusAuthorization HTTP header
using (new OperationContextScope((IContextChannel)billingClient))
{
    string token = tokenProvider.GetTokenAsync(address, TimeSpan.FromMinutes(20)).Result.TokenString;
    var httpRequestProperty = new HttpRequestMessageProperty();
    httpRequestProperty.Headers.Add("ServiceBusAuthorization", token);
    OperationContext.Current.OutgoingMessageProperties.Add(HttpRequestMessageProperty.Name, httpRequestProperty);

    string response = billingClient.TestMessage("From .NET Core Sender!");
    Console.WriteLine("Listener replied: " + response);
}
commented

@dlstucki , appreciate your code sample a lot! This saved a lot of my time. Thank you!

This could be added as a sample.

@dlstucki Not to necro this issue, but I was wondering if you know the current status of this tech? Do you know if CoreWCF or any other effort has added support for relay endpoints on for the WCF Relay Listener (to use your classification) running on .NET 5+?

My employer is looking at some changes to our ecosystem, so we need to re-evaluate the choices we made last time.

(apologies to necro this but just incase anyone stumbles across this like I did!)

This worked like a charm for our current setup; where the main system is fully migrated to .net6 however numerous distributed applications still running .net framwork still needing to commincate over the existing WCF relay.

The only change we made was to use a IClientMessageInspector to append the security token as this allowed us to use asynchronous calls as the OperationContextScope didn't play too well with asynchronous code.

Followed this guide with a few tweaks to achieve this:
https://medium.com/@tadhg.j.mulkern/using-a-bearer-token-in-net-core-with-wcf-part-1-8db2c54113f8