WCF Proxy call is not registering with server? - wcf

I've got a service and have verified using "netstat -anb" that when the service is running, it's listening on the correct port (8040). The service contract contains the following contract:
[OperationContract]
bool RegisterPlayer();
The service class itself implements the contract explicitly:
bool IMechService.RegisterPlayer()
{
if (P1 != null)
{
P1 = OperationContext.Current.GetCallbackChannel<IMechServiceCallback>();
return true;
}
else if (P2 != null)
{
P2 = OperationContext.Current.GetCallbackChannel<IMechServiceCallback>();
return true;
}
return false;
}
And the svcutil generated proxy creates the following method:
public bool RegisterPlayer()
{
return base.Channel.RegisterPlayer();
}
This code attempts to generate a proxy and call the method. I've tried both using DuplexChannelFactory and the svcutil generated proxy class, and both give the same results:
client = new MechServiceClient(new InstanceContext(this));
//client = DuplexChannelFactory<IMechService>.CreateChannel(this, new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:8040/MechService"));
client.RegisterPlayer();
Code execution reaches the RegisterPlayer in the proxy class, but proceeds to time out, never running RegisterPlayer on the service. Unfortunately, as it's just timing out, I'm not getting any exceptions or errors to help indicate where to look for issues. So far, I've verified the service is running and appears to be listening on port 8040 using "netstat -anb", and I've established that the mex endpoint is working as intended and publishing metadata. I turned off Windows Firewall. I've also created a separate test project with much simpler implementations to verify I was doing the steps correctly, and the simpler test project works fine. I'm out of ideas for what's causing this to fail, and any advice would be appreciated.

Have you tried setting the ConcurrencyMode to ConcurrencyMode.Multiple?
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
class MechServiceImpl : IMechService
{
// ..
}
The default concurrency mode for a service is ConcurrencyMode.Single, which can cause complications with callbacks.

Andrew's suggestion to logging helped, basically what fixed it was declaring my OperationContracts to isoneway=true.

Related

WCF Task based asynchronous callback

I have created WCF service in VS2015:
[ServiceContract(CallbackContract = typeof(IMyCallback))]
public interface IMyService { }
IMyCallback looks like:
[ServiceContract]
public interface IMyCallback {
[OperationContract]
Task<string> OnServerEvent(UserAppEventData evData);
I've built the server, run it, then added service reference (by right click on solution explorer).
The client object is defined as
[CallbackBehaviorAttribute(
ConcurrencyMode = ConcurrencyMode.Reentrant,
IncludeExceptionDetailInFaults = true,
UseSynchronizationContext = true,
ValidateMustUnderstand = true
)]
public class QMyClient : IMyCallback { }
Automatically generated interface implementation made method in sync manner:
public string OnServerEvent(UserAppEventData evData) { }
This code does't work (and isn't asynchronous) and hangs client at OnServerEvent.
When I changed code manuallly to
public async Task<string> OnServerEvent(UserAppEventData evData)
and have done the same in auto generated "service references\...\Reference.cs, all works fine. But I don't want to change Referenece.cs every time I'm updating Service Reference.
Is there any method to force "Update Service Reference" make TBA OperationContractAttribute on callback?
At ordinary WCF service direction everything works OK, VS generates task based operations.
By default the service reference you've added to solution doesn't have asynchronous operations, but you can enable them and decide which option you use for your async methods - task-based or old-fashion asynchronous. This option is available in Advanced settings for service reference.
If you're using a svcutil tool, it will create the task-based methods by default, however, you can change that behavior by some flags like /async or /syncOnly.
What #VMAtm suggested will work out just fine.
I think, you could also use ChannelFactory for this scenario. It is very flexible and you can then await on the service operations from client side. Additional benefit, you don't need to modify client when there are these kind of changes on service side.
Something like:
var channelFactory = new ChannelFactory<IService>(
"WSHttpBinding_IService" // endpoint name
);
IService channel = channelFactory.CreateChannel();
string result = await channel.OnServerEvent();
Console.WriteLine(result);
Please note that for this scenario, you will have to import common interface library to client side as dll because then it will need to know about contracts and data contracts.

Call Service Fabric service from console application using WCF HTTPS endpoint

I have a service hosted in a Service Fabric cluster in Azure (not locally) and I'm trying to call a method in it using a console application on my local machine. Using WCF for communication, I have a HTTPS endpoint set up in my application on a specific port, and have configured load balancing rules for the port in the Azure portal. The cluster has 6 nodes and the application is the only one deployed on the cluster.
Have followed the ServiceFabric.WcfCalc on GitHub (link), which works on a local cluster using HTTP endpoints, but can't call a method on the service using HTTPS endpoints once it has been deployed. What do I need to do to get it working? Have tried following the example here but don't know how to configure this for HTTPS with a service on multiple nodes for a console application to access.
Thanks in advance.
EDIT Here's my client code which I am using to call the service method. I pass the fabric:/ URI into the constructor here.
public class Client : ServicePartitionClient<WcfCommunicationClient<IServiceInterface>>, IServiceInterface
{
private static ICommunicationClientFactory<WcfCommunicationClient<IServiceInterface>> communicationClientFactory;
static Client()
{
communicationClientFactory = new WcfCommunicationClientFactory<IServiceInterface>(
clientBinding: new BasicHttpBinding(BasicHttpSecurityMode.Transport));
}
public Client(Uri serviceUri)
: this(serviceUri, ServicePartitionKey.Singleton)
{ }
public Client(
Uri serviceUri,
ServicePartitionKey partitionKey)
: base(
communicationClientFactory,
serviceUri,
partitionKey)
{ }
public Task<bool> ServiceMethod(DataClass data)
{
try
{
//It hangs here
return this.InvokeWithRetry((c) => c.Channel.ServiceMethod(data));
}
catch (Exception)
{
throw;
}
}
}
When debugging my console application on my local machine, the application hangs on the InvokeWithRetry call which calls the method in my service in Service Fabric. The application does not throw any exceptions and does not return to the debugger in Visual Studio.
Make sure you run every service instance /replica with a unique url.
Make sure you call the WebHttpBinding constructor using WebHttpSecurityMode.Transport.
Make sure you register the url using the same port number (443 likely) as in you service manifest endpoint declaration.
Make sure the endpoint is configured as HTTPS.
The warning you see in Service Fabric is telling you that there is already another service registered to listen on port 443 on your nodes. This means that Service Fabric fails to spin up your service (since it throws an exception internally when it is trying to register the URL with http.sys). You can change the port for your service to something else that will not conflict with the existing service, e.g.:
<Resources>
<Endpoint Name="CalculatorEndpoint" Protocol="https" Type="Input" Port="44330" />
</Endpoints>
If you log in to Service Fabric Explorer on https://{cluster_name}.{region}.cloudapp.azure.com:19080 you should be able to see what other applications and services are running there. If you expand services all the way down to node you should be able to see the registered endpoints, including ports, for existing services.
Bonus
You can query the cluster using FabricClient for all registered endpoints
var fabricClient = new FabricClient();
var applicationList = fabricClient.QueryManager.GetApplicationListAsync().GetAwaiter().GetResult();
foreach (var application in applicationList)
{
var serviceList = fabricClient.QueryManager.GetServiceListAsync(application.ApplicationName).GetAwaiter().GetResult();
foreach (var service in serviceList)
{
var partitionListAsync = fabricClient.QueryManager.GetPartitionListAsync(service.ServiceName).GetAwaiter().GetResult();
foreach (var partition in partitionListAsync)
{
var replicas = fabricClient.QueryManager.GetReplicaListAsync(partition.PartitionInformation.Id).GetAwaiter().GetResult();
foreach (var replica in replicas)
{
if (!string.IsNullOrWhiteSpace(replica.ReplicaAddress))
{
var replicaAddress = JObject.Parse(replica.ReplicaAddress);
foreach (var endpoint in replicaAddress["Endpoints"])
{
var endpointAddress = endpoint.First().Value<string>();
Console.WriteLine($"{service.ServiceName} {endpointAddress} {endpointAddress}");
}
}}}}}
Just run that with the proper FabricClient credentials (if it is a secured cluster) and you should see it listing all endpoints for all services there. That should help you find the one that has an endpoint for :443

How Can I stop a WCF Service (with net.MSMQ binding) Hosted on IIS from another Windows Application

I have a WCF Service Using MSMQ hosted on IIS. I want to create a windows application which can stop WCF Service from picking MSMQ message. Once I have seen the MSMQ message in the queue I need to click a button and Start the WCF service to pick the message in MSMQ. Code sample would be apperciated.
IIS is not an appropriate container to host a MSMQ client in. This is because when the app pool unloads during times of low traffic the queue client also unloads. This behaviour is automatic and you don't have any control over it.
It would be far better to host your client in a windows service. However, the kind of "consume-on-demand" functionality you require is not easy to achieve and certainly is not supported by the standard bindings.
The best I can suggest is consume the message as soon as it's received and persist it somewhere until the user clicks the button, upon which you do whatever you want as the data in the message is already available.
I was able to solve this problem by applying a workaround. I created another queue in a different machine. Changed the address of the WCF client endpoint address to this queue in config. I created another external application which moved the message from the alternate queue to the actual queue. Thus the behavior of stopping IIS hosted WCF service with MSMQ binding was achieved
Stopping the "Net.Msmq Listener Adapter" Windows service and the "Windows Process Activation Service" will stop the messages from being pulled out of the queue. Starting the services back up will causes the messages to be pulled from the queue again. I'm doing this manually, rather than through another application, but I'd assume you could do it through another application as well. I haven't tested this completely, but something like this would probably work:
Dictionary<string,List<string>> runningDependentServices = new Dictionary<string,List<string>>();
private void StartMsmqBinding()
{
StartService("WAS");
StartService("NetMsmqActivator");
}
private void StopMsmqBinding()
{
StopService("NetMsmqActivator");
StopService("WAS");
}
private void StartService(string serviceName)
{
List<string> previouslyRunningServices = null;
var sc = new ServiceController();
sc.ServiceName = serviceName;
if (runningDependentServices.ContainsKey(serviceName))
{
previouslyRunningServices = runningDependentServices[serviceName];
}
try
{
sc.Start();
sc.WaitForStatus(ServiceControllerStatus.Running);
if(previouslyRunningServices != null)
{
previouslyRunningServices.ForEach(a =>
{
var serviceController = new System.ServiceProcess.ServiceController() { ServiceName = a };
serviceController.Start();
serviceController.WaitForStatus(ServiceControllerStatus.Running);
});
}
}
catch (InvalidOperationException)
{
}
}
private void StopService(string serviceName)
{
var sc = new System.ServiceProcess.ServiceController() { ServiceName = serviceName };
runningDependentServices[serviceName] = sc.DependentServices.Where(a => a.Status == System.ServiceProcess.ServiceControllerStatus.Running).Select(a => a.ServiceName).ToList();
if (sc.CanStop)
{
try
{
sc.Stop();
sc.WaitForStatus(ServiceControllerStatus.Stopped);
}
catch (InvalidOperationException)
{
}
}
}
I'd think a similar approach would work for Net.Tcp binding. You'd probably have to stop the "Net.Tcp Listener Adapter" Windows service (ServiceName: "NetTcpActivator") and the "Windows Process Activation Service" in that case.

Debugging a WCF project

I constructed a little solution containing 4 projects:
Contract: contains my (t4 generated) entities and interface to my service
Service: contains my (t4 generated) context and implementation of my service
Host: contains the bare minimum to host a service
ServiceHost host = new ServiceHost(typeof(InleerAppService));
try
{
host.Open();
Console.WriteLine("The service is ready!");
Console.ReadKey();
host.Close();
}
catch (CommunicationException cex)
{
Console.WriteLine(cex.Message);
}
Client:
var factory = new ChannelFactory("InleerAppService");
IInleerAppService service = factory.CreateChannel();
var result = service.ReturnInput("test string"); // just returns the input string, this works!
Console.WriteLine(result);
var result2 = service.GetAllCompanies(); // this doesn't and crashes the client
foreach (Company c in result2)
{
Console.WriteLine(c.Name);
}
Console.ReadKey();
You understand I would like to figure out what is going. But I don't really understand how I can debug this. First I start the host with ctrl+F5, then the client. But this doesn't allow me to debug. How should I go to that, using this setup? I know there are more ways to work with services, but for this part I'd just want to focus on this setup.
You can setup the solution to start multiple projects and just hit F5. To set this up, right click on the solution and go to properties. Select start up project under common properties. And choose both your service and client projects for startup.
Another way to debug is to select the service project, right click and go to debug -> start new instance. Next, do the same thing for client project. Now you should have both service and client projects running under debug mode.

Connecting via named pipe from windows service (session#0) to desktop app (session #1)

Given:
- the application - desktop GUI (WPF) .NET app
- windows service watching for application (.NET also)
The windows service periodically "pings" application to get sure it's healthy (and if it's not winservice will restart it).
I was going to implement "pinging" via named pipes. To make things simpler I decided to do it with WCF. The application hosts a WCF-service (one operation Ping returning something). The windows service is a client for this WCF-service, invokes it periodically based on a timer.
That's all in Windows 7.
Windows service is running under LocalService (in session#0).
Desktop application is running under currently logged in user (in session#1).
The problem:
Windows service can't see WCF endpoint (with NetNamedPipeBinding) created in and being listened in desktop application. That means that on call via wcf proxy I get this exception: "The pipe endpoint 'net.pipe://localhost/HeartBeat' could not be found on your local machine"
I'm sure code is ok, because another desktop application (in session#1) can see the endpoint.
Obviously here I'm dealing with some security stuff for Win32 system object isolation.
But I believe there should be a way to workaround restrictions I've encountered with.
I can sacrifice WCF approach and go the raw NamedPipe way.
An easier solution might be to use a WCF duplex contract with the Windows service hosting the WCF service. The client App would call an operation on the service to register itself, when it starts up. The Ping would then be an operation invoked periodically by the service on the client's callback contract, to which the App would respond.
Service visibility works this way round, because the Windows service can run with SeCreateGlobalPrivilege, and so the shared memory object via which the pipe name is published by the service can be created in the Global kernel namespace, visible to other sessions. Interactive applications can't easily get that privilege in Windows7, so WCF services in such applications fall back to publishing the pipe in the Local kernel namespace, visible only within their own session.
Finally I've found a solution - using Named Pipes from System.IO.Pipes directly. It's seems that WCF's pipes support implementation doesn't use System.IO.Pipes.
Server:
using (var pipeServer = new NamedPipeServerStream("mypipe", PipeDirection.Out, 1))
{
try
{
while (true)
{
// #1 Connect:
try
{
pipeServer.WaitForConnection();
}
catch (ObjectDisposedException)
{
yield break;
}
if (ae.IsCanceled())
return;
// #2: Sending response:
var response = Encoding.ASCII.GetBytes(DateTime.Now.ToString());
try
{
pipeServer.Write(response, 0, response.Length);
}
catch (ObjectDisposedException)
{
return;
}
// #3: Disconnect:
pipeServer.Disconnect();
}
}
finally
{
if (pipeServer.IsConnected)
pipeServer.Disconnect();
}
}
Client:
using (var pipeClient = new NamedPipeClientStream(".", "mypipe", PipeDirection.In))
{
try
{
try
{
pipeClient.Connect(TIMEOUT);
}
catch(TimeoutException ex)
{
// nobody answers to us
continue;
}
using (var sr = new StreamReader(pipeClient))
{
string temp;
while ((temp = sr.ReadLine()) != null)
{
// got response
}
}
}
catch(Exception ex)
{
// pipe error
throw;
}
}