How to set more than one fallback endpoints in WCF - wcf

How to set fall-back endpoint. I'm having more than one endpoint specified in the conifg file like follows. If the service was not accessible, then my client should check the next address specified in the list.
Client Configuration File:
<client>
<endpoint address="http://192.168.1.4/SampleAppWeb/Services/SampleAppService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ISampleAppService"
contract="SampleAppServiceReference.ISampleAppService" name="BasicHttpBinding_ISampleAppService" />
<endpoint address="http://172.168.12.121/SampleAppWeb/Services/SampleAppService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ISampleAppService"
contract="SampleAppServiceReference.ISampleAppService" name="BasicHttpBinding_ISampleAppService1" />
<endpoint address="http://127.0.0.1/Services/SampleAppWeb/SampleAppService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ISampleAppService"
contract="SampleAppServiceReference.ISampleAppService" name="BasicHttpBinding_ISampleAppService2" />
<endpoint address="http://172.168.111.115/Services/SampleAppWeb/SampleAppService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ISampleAppService"
contract="SampleAppServiceReference.ISampleAppService" name="BasicHttpBinding_ISampleAppService3" />
</client>
Code Behind:
var pass = new SampleAppServiceReference.SampleAppServiceClient("BasicHttpBinding_ISampleAppService3");

WCF itself doesn't have any built-in feature for this, but you can easily create a class which will do the retrying for you. The example below shows one such way. Notice that if your binding uses session, you may need to recreate the client (instead of simply reusing it), as its channel will likely be faulted if an error happens.
public class StackOverflow_4968244
{
[ServiceContract]
public interface ITest
{
[OperationContract]
int Add(int x, int y);
}
public class Service : ITest
{
public int Add(int x, int y)
{
Console.WriteLine("Request at service: {0}", OperationContext.Current.Host.BaseAddresses[0]);
if (new Random().Next(3) == 0)
{
throw new InvalidOperationException("Random error");
}
return x + y;
}
}
public class Client : ClientBase<ITest>, ITest
{
public Client(string address) : base(new BasicHttpBinding(), new EndpointAddress(address)) { }
public int Add(int x, int y)
{
return this.Channel.Add(x, y);
}
}
public class SafeClient : ITest
{
List<Client> clients;
public SafeClient(params Client[] clients)
{
this.clients = new List<Client>(clients);
}
public int Add(int x, int y)
{
foreach (var client in this.clients)
{
try
{
return client.Add(x, y);
}
catch (CommunicationException)
{
Console.WriteLine("Error calling client {0}, retrying with next one", client.Endpoint.Address.Uri);
}
}
throw new InvalidOperationException("All services seem to be down");
}
}
public static void Test()
{
string baseAddress1 = "http://" + Environment.MachineName + ":8000/Service";
string baseAddress2 = "http://" + Environment.MachineName + ":8001/Service";
string baseAddress3 = "http://" + Environment.MachineName + ":8002/Service";
ServiceHost host1 = new ServiceHost(typeof(Service), new Uri(baseAddress1));
ServiceHost host2 = new ServiceHost(typeof(Service), new Uri(baseAddress2));
ServiceHost host3 = new ServiceHost(typeof(Service), new Uri(baseAddress3));
host1.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "");
host2.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "");
host3.AddServiceEndpoint(typeof(ITest), new BasicHttpBinding(), "");
host1.Open();
host2.Open();
host3.Open();
Console.WriteLine("Hosts opened");
SafeClient safeClient = new SafeClient(
new Client(baseAddress1),
new Client(baseAddress2),
new Client(baseAddress3));
for (int i = 0; i < 20; i++)
{
Console.WriteLine(safeClient.Add(i, 10));
}
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host1.Close();
host2.Close();
host3.Close();
}
}

Related

How to read WCF message headers in duplex callback?

In a normal WCF request/reply contract, you can read the message headers using something like:
OperationContract.Current.IncomingMessageHeaders
What I can't figure out is how to do this on the callback side of a duplex contract. Inside the callback implementation OperationContext.Current is null.
Edit 4/5/2013:
I'm using a custom binding based on net.tcp, but with a lot of customizations. For example, using protocol buffers message encoding rather than Xml. Also there is some custom security.
What binding are you using? In the SSCCE below the context is not null on the callback implementation.
public class StackOverflow_15769719
{
[ServiceContract(CallbackContract = typeof(ICallback))]
public interface ITest
{
[OperationContract]
string Hello(string text);
}
[ServiceContract]
public interface ICallback
{
[OperationContract(IsOneWay = true)]
void OnHello(string text);
}
public class Service : ITest
{
public string Hello(string text)
{
ICallback callback = OperationContext.Current.GetCallbackChannel<ICallback>();
ThreadPool.QueueUserWorkItem(delegate
{
callback.OnHello(text);
});
return text;
}
}
class MyCallback : ICallback
{
AutoResetEvent evt;
public MyCallback(AutoResetEvent evt)
{
this.evt = evt;
}
public void OnHello(string text)
{
Console.WriteLine("[callback] Headers: ");
foreach (var header in OperationContext.Current.IncomingMessageHeaders)
{
Console.WriteLine("[callback] {0}", header);
}
Console.WriteLine("[callback] OnHello({0})", text);
evt.Set();
}
}
public static void Test()
{
bool useTcp = false;
string baseAddress = useTcp ?
"net.tcp://" + Environment.MachineName + ":8000/Service" :
"http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
Binding binding = useTcp ?
(Binding)new NetTcpBinding(SecurityMode.None) :
new WSDualHttpBinding(WSDualHttpSecurityMode.None)
{
ClientBaseAddress = new Uri("http://" + Environment.MachineName + ":8888/Client")
};
host.AddServiceEndpoint(typeof(ITest), binding, "");
host.Open();
Console.WriteLine("Host opened");
AutoResetEvent evt = new AutoResetEvent(false);
MyCallback callback = new MyCallback(evt);
DuplexChannelFactory<ITest> factory = new DuplexChannelFactory<ITest>(
new InstanceContext(callback),
binding,
new EndpointAddress(baseAddress));
ITest proxy = factory.CreateChannel();
Console.WriteLine(proxy.Hello("foo bar"));
evt.WaitOne();
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}

Having WCF callback only for binding that supports it

I am trying to create a WCF service, that has a webHttpBinding endpoint (for Java clients) and a netTcpBinding endpoint (for .NET clients).
With the netTcpBinding endpoint I would like to be able to use callbacks in order to be alerted to events, but when I try to configure this, WCF complains because the service also has the webHttpBinding endpoint, which doesn't support callbacks.
Is there a way of having the callback utilised by one endpoint but not another?
No, the binding will validate that it can honor the contract; if the contract is a duplex contract (i.e., it specifies a CallbackContract) but the binding can't do duplex, then it will throw during validation.
What you can do is to have a base contract which is used by the webHttpBinding endpoint, and another contract (this time a duplex one), derived from the first, which is used by the netTcpBinding endpoint.
The code below shows an example of such contract arrangement.
public class StackOverflow_7341463
{
[ServiceContract]
public interface ICalc
{
[OperationContract, WebGet]
int Add(int x, int y);
[OperationContract, WebGet]
int Subtract(int x, int y);
[OperationContract, WebGet]
int Multiply(int x, int y);
[OperationContract, WebGet]
int Divide(int x, int y);
}
[ServiceContract(CallbackContract = typeof(ICalcNotifications))]
public interface INotifyingCalc : ICalc
{
[OperationContract]
void Connect();
[OperationContract]
void Disconnect();
}
[ServiceContract]
public interface ICalcNotifications
{
[OperationContract(IsOneWay = true)]
void OperationPerformed(string text);
}
public class Service : INotifyingCalc
{
static List<ICalcNotifications> clients = new List<ICalcNotifications>();
#region ICalc Members
public int Add(int x, int y)
{
this.NotifyOperation("Add", x, y);
return x + y;
}
public int Subtract(int x, int y)
{
this.NotifyOperation("Subtract", x, y);
return x - y;
}
public int Multiply(int x, int y)
{
this.NotifyOperation("Multiply", x, y);
return x * y;
}
public int Divide(int x, int y)
{
this.NotifyOperation("Divide", x, y);
return x / y;
}
#endregion
#region INotifyingCalc Members
public void Connect()
{
var callback = OperationContext.Current.GetCallbackChannel<ICalcNotifications>();
clients.Add(callback);
}
public void Disconnect()
{
var callback = OperationContext.Current.GetCallbackChannel<ICalcNotifications>();
clients.Remove(callback);
}
#endregion
private void NotifyOperation(string operationName, int x, int y)
{
foreach (var client in clients)
{
client.OperationPerformed(string.Format("{0}({1}, {2})", operationName, x, y));
}
}
}
class MyCallback : ICalcNotifications
{
public void OperationPerformed(string text)
{
Console.WriteLine("Operation performed: {0}", text);
}
}
public static void Test()
{
string baseAddressTcp = "net.tcp://" + Environment.MachineName + ":8008/Service";
string baseAddressHttp = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddressHttp), new Uri(baseAddressTcp));
host.AddServiceEndpoint(typeof(ICalc), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());
host.AddServiceEndpoint(typeof(INotifyingCalc), new NetTcpBinding(SecurityMode.None), "");
host.Open();
Console.WriteLine("Host opened");
var factory = new DuplexChannelFactory<INotifyingCalc>(
new InstanceContext(new MyCallback()),
new NetTcpBinding(SecurityMode.None),
new EndpointAddress(baseAddressTcp));
var proxy = factory.CreateChannel();
proxy.Connect();
Console.WriteLine("Proxy connected");
Console.WriteLine(new WebClient().DownloadString(baseAddressHttp + "/Add?x=4&y=7"));
Console.WriteLine(new WebClient().DownloadString(baseAddressHttp + "/Multiply?x=44&y=57"));
Console.WriteLine(new WebClient().DownloadString(baseAddressHttp + "/Divide?x=432&y=16"));
proxy.Disconnect();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
((IClientChannel)proxy).Close();
factory.Close();
host.Close();
}
}

How can I programmatically get the binding that my client proxy is using?

I have a WCF proxy generated at runtime with DuplexChannelFactory.
How can I access the binding information given only the service interface returned from DuplexChannelFactory?
I can get most stuff by casting to an IClientChannel, but I can't seem to find binding info on there. The closest I can get is IClientChannel.RemoteAddress which is an endpoint, but that doesn't seem to have binding info either. :-/
You can't (directly). There are a few things which you can get from the channel, such as the message version (channel.GetProperty<MessageVersion>()), and other values. But the binding isn't one of those. The channel is created after the binding is "deconstructed" (i.e., expanded into its binding elements, while each binding element can add one more piece to the channel stack.
If you want to have the binding information in the proxy channel, however, you can add it yourself, using one of the extension properties of the context channel. The code below shows one example of that.
public class StackOverflow_6332575
{
[ServiceContract]
public interface ITest
{
[OperationContract]
int Add(int x, int y);
}
public class Service : ITest
{
public int Add(int x, int y)
{
return x + y;
}
}
static Binding GetBinding()
{
BasicHttpBinding result = new BasicHttpBinding();
return result;
}
class MyExtension : IExtension<IContextChannel>
{
public void Attach(IContextChannel owner)
{
}
public void Detach(IContextChannel owner)
{
}
public Binding Binding { get; set; }
}
static void CallProxy(ITest proxy)
{
Console.WriteLine(proxy.Add(3, 5));
MyExtension extension = ((IClientChannel)proxy).Extensions.Find<MyExtension>();
if (extension != null)
{
Console.WriteLine("Binding: {0}", extension.Binding);
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(ITest), GetBinding(), "");
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(GetBinding(), new EndpointAddress(baseAddress));
ITest proxy = factory.CreateChannel();
((IClientChannel)proxy).Extensions.Add(new MyExtension { Binding = factory.Endpoint.Binding });
CallProxy(proxy);
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}

WCF: How to actually get to use WSHttpBinding? I get exceptions instead

I created a WCF Service:
Shared.dll:
[ServiceContract(ConfigurationName = "ICalculator")]
public interface ICalculator
{
[OperationContract()]
int Add(int a, int b);
}
Server:
[ServiceBehavior()]
public class Calculator : ICalculator
{
public int Add(int a, int b) { return a + b; }
}
Client (Attempt #1):
public class CalculatorClient : ClientBase<ICalculator>, ICalculator
{
private static Binding binding = new WSHttpBinding("MyConfig");
private static EndpointAddress remoteAddress = new EndpointAddress(...);
public CalculatorClient() : base(binding, remoteAddress) { }
public int Add(int a, int b)
{
return Channel.Add(a, b); //Exception
}
}
Client (Attempt #2): -- Note: I added a Service Reference instead of creating a CalculatorClient myself (.NET created it for me).
static void Main(string[] args)
{
Binding binding = new WSHttpBinding("MyConfig");
EndpointAddress remoteAddress = new EndpointAddress(...);
CalculatorClient client = new CalculatorClient(binding, remoteAddress);
int result = client.Add(5, 4); //Exception
}
Client (Attempt #3): -- I changed it to be a BasicHttpBinding() instead
static void Main(string[] args)
{
Binding binding = new BasicHttpBinding("MyConfig");
EndpointAddress remoteAddress = new EndpointAddress(...);
CalculatorClient client = new CalculatorClient(binding, remoteAddress);
int result = client.Add(5, 4); //This works!
}
app.config:
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="MyConfig" /> <!-- did not add anything to this yet -->
</wsHttpBinding>
</bindings>
</system.serviceModel>
The exception I get is: Content Type application/soap+xml; charset=utf-8 was not supported by service http://localhost/CalculatorService.svc. The client and service bindings may be mismatched. I don't see how they can be mismatched when I use a shared dll file between my server and client. The BasicHttpBinding works, just not the WSHttpBinding (I haven't even attempted WS2007HttpBinding.
Exception: [System.ServiceModel.ProtocolException]
{"Content Type application/soap+xml; charset=utf-8 was not supported by service http://localhost/CalculatorService.svc. The client and service bindings may be mismatched."}
Inner Exception: [System.Net.WebException]
The remote server returned an error: (415) Cannot process the message because the content type 'application/soap+xml; charset=utf-8' was not the expected type 'text/xml; charset=utf-8'..
You need to set the security to be used on the WSHttpBinding
http://msdn.microsoft.com/en-us/library/ms731884(v=VS.90).aspx
Updated with Sample Client/Server WSHttpBinding, default security
Client
class Program
{
static void Main(string[] args)
{
var calcClient = new CalcClient();
int i = 1;
int j = 2;
Console.WriteLine("Result of Adding {0} and {1} is {2}", i, j, calcClient.Add(i, j));
Console.ReadKey();
}
}
public class CalcClient : ICalculator
{
public CalcClient()
{
CalcProxy = ChannelFactory.CreateChannel(new WSHttpBinding(), new EndpointAddress("http://localhost:5050/CalcServer"));
}
ICalculator CalcProxy { get; set; }
public int Add(int a, int b)
{
return CalcProxy.Add(a, b);
}
}
Server
class Program
{
static void Main(string[] args)
{
var host = new ServiceHost(typeof (CalcSvr));
host.AddServiceEndpoint(typeof (ICalculator), new WSHttpBinding(), "http://localhost:5050/CalcServer");
host.Open();
Console.WriteLine("Server Running");
Console.ReadKey();
}
}

NullReference Exception is thrown while getting a callback channel

I am trying to get along with WCF's duplex contracts. A code from this article
(http://msdn.microsoft.com/en-us/library/ms731184.aspx)
ICalculatorDuplexCallback callback = null;
callback = OperationContext.Current.GetCallbackChannel();
throws a NullReferenceException. So how can i manage this?
Thanks for your attention!
Have you decorated the interface for ICalculatorDuplex
with
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples", SessionMode=SessionMode.Required,
CallbackContract=typeof(ICalculatorDuplexCallback))]
When a service receives a message it looks at a replyTo element in the message to determine where to reply to, I would guess that if your missing the callback contract attribute it, it would result in you getting a NullReferenceException, as it doesn't know where to replyTo.
I've just ran quickly through the example.
The code for my service is :
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
namespace DuplexExample
{
// Define a duplex service contract.
// A duplex contract consists of two interfaces.
// The primary interface is used to send messages from client to service.
// The callback interface is used to send messages from service back to client.
// ICalculatorDuplex allows one to perform multiple operations on a running result.
// The result is sent back after each operation on the ICalculatorCallback interface.
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples", SessionMode=SessionMode.Required,
CallbackContract=typeof(ICalculatorDuplexCallback))]
public interface ICalculatorDuplex
{
[OperationContract(IsOneWay=true)]
void Clear();
[OperationContract(IsOneWay = true)]
void AddTo(double n);
[OperationContract(IsOneWay = true)]
void SubtractFrom(double n);
[OperationContract(IsOneWay = true)]
void MultiplyBy(double n);
[OperationContract(IsOneWay = true)]
void DivideBy(double n);
}
// The callback interface is used to send messages from service back to client.
// The Equals operation will return the current result after each operation.
// The Equation opertion will return the complete equation after Clear() is called.
public interface ICalculatorDuplexCallback
{
[OperationContract(IsOneWay = true)]
void Equals(double result);
[OperationContract(IsOneWay = true)]
void Equation(string eqn);
}
// Service class which implements a duplex service contract.
// Use an InstanceContextMode of PerSession to store the result
// An instance of the service will be bound to each duplex session
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class CalculatorService : ICalculatorDuplex
{
double result;
string equation;
ICalculatorDuplexCallback callback = null;
public CalculatorService()
{
result = 0.0D;
equation = result.ToString();
callback = OperationContext.Current.GetCallbackChannel<ICalculatorDuplexCallback>();
}
public void Clear()
{
callback.Equation(equation + " = " + result);
result = 0.0D;
equation = result.ToString();
}
public void AddTo(double n)
{
result += n;
equation += " + " + n;
callback.Equals(result);
}
public void SubtractFrom(double n)
{
result -= n;
equation += " - " + n;
callback.Equals(result);
}
public void MultiplyBy(double n)
{
result *= n;
equation += " * " + n;
callback.Equals(result);
}
public void DivideBy(double n)
{
result /= n;
equation += " / " + n;
callback.Equals(result);
}
}
class Program
{
static void Main()
{
var host = new ServiceHost(typeof(CalculatorService));
host.Open();
Console.WriteLine("Service is open");
Console.ReadLine();
}
}
}
My application config looks like :
<?xml version="1.0" encoding="utf-8" ?>
<services>
<service behaviorConfiguration="NewBehavior" name="DuplexExample.CalculatorService">
<endpoint address="dual" binding="wsDualHttpBinding" bindingConfiguration=""
contract="DuplexExample.ICalculatorDuplex" />
<endpoint address="mex" binding="mexHttpBinding" bindingConfiguration=""
contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8081/duplex" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
I then used the host address in the config file to create a service reference named CalculatorService.
so my client looks like :
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using Client.CalculatorService;
namespace Client
{
class Program
{
static void Main(string[] args)
{
var context = new InstanceContext(new CallbackHandler());
var client = new CalculatorDuplexClient(context);
Console.WriteLine("Press <ENTER> to terminate client once the output is displayed.");
Console.WriteLine();
// Call the AddTo service operation.
var value = 100.00D;
client.AddTo(value);
// Call the SubtractFrom service operation.
value = 50.00D;
client.SubtractFrom(value);
// Call the MultiplyBy service operation.
value = 17.65D;
client.MultiplyBy(value);
// Call the DivideBy service operation.
value = 2.00D;
client.DivideBy(value);
// Complete equation
client.Clear();
Console.ReadLine();
//Closing the client gracefully closes the connection and cleans up resources
client.Close();
}
}
// Define class which implements callback interface of duplex contract
public class CallbackHandler : ICalculatorDuplexCallback
{
public void Result(double result)
{
Console.WriteLine("Result({0})", result);
}
public void Equation(string eqn)
{
Console.WriteLine("Equation({0})", eqn);
}
#region ICalculatorDuplexCallback Members
public void Equals(double result)
{
Console.WriteLine("Equals{0} ",result );
}
#endregion
}
}