I am creating a selfhosted WCF Service.
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface IStateChecker
{
[OperationContract]
void SetState(string state);
}
This is my Service:
public class StateCheckerService : IStateChecker
{
public void SetState(string state)
{
Console.WriteLine($"{DateTime.Now.ToString("dd.MM.yyyy HH:mm:sss")} : {state}");
}
}
And this my Implementation:
//Define baseaddres:
Uri baseAddress = new Uri("http://localhost:8000/ServiceModelSamples/Service");
//create host:
ServiceHost selfHost = new ServiceHost(typeof(StateCheckerService), baseAddress);
try
{
//Add endpoint to host:
selfHost.AddServiceEndpoint(typeof(IStateChecker), new WSHttpBinding(), "StateCheckerService");
//Add metadata exchange:
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
selfHost.Description.Behaviors.Add(smb);
selfHost.Faulted += SelfHost_Faulted;
//Start service
selfHost.Open();
Console.WriteLine("Starting Service...");
if (selfHost.State == CommunicationState.Opened)
{
Console.WriteLine("The service is ready.");
}
Console.WriteLine("Press <ENTER> to terminate service.");
Console.ReadLine();
//Shutdown service
selfHost.Close();
}
catch (CommunicationException ce)
{
Console.WriteLine("An exception occurred: {0}", ce.Message);
selfHost.Abort();
}
private static void SelfHost_Faulted(object sender, EventArgs e)
{
ServiceHost host = sender as ServiceHost;
Console.WriteLine(host?.State);
Console.WriteLine(e?.ToString());
host?.Open();
}
Now when it comes to the client I get an error.
try
{
//Works using the ServiceReference (wsdl ... created by VisualStudio):
using (StateCheckerServiceReference.StateCheckerClient client = new StateCheckerClient())
{
client.SetState("Test");
}
//Does not work:
EndpointAddress endpointAddress = new EndpointAddress("http://localhost:8000/ServiceModelSamples/Service");
using (ChannelFactory<IStateCheckerChannel> factory = new ChannelFactory<IStateCheckerChannel>("WSHttpBinding_IStateChecker", endpointAddress))
{
using (IStateCheckerChannel channel = factory.CreateChannel(endpointAddress))
{
channel?.SetState("Test");
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Exception:
The communication object, "System.ServiceModel.Channels.ServiceChannel",
cannot be used for communication because it is in the Faulted state.
I never enter SelfHost_Faulted nor are there any Exceptions on my Service
I am doing this because I want to change the Endpoint the client should connect to at runtime.
If I'm doin it wrong please tell me. Otherwise any hints on what is wrong with my code are highly appreciated.
The issue is quite trivial, but hidden by the WCF infrastructure (strange implementation of a standard pattern).
If you change
channel?.SetState("Test");
to
try
{
channel?.SetState("Test");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
you will see 2 messages:
There was no endpoint listening at http://localhost:8000/ServiceModelSamples/Service that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
and
The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state.
The first one is the real exception (of type EndpointNotFoundException) caught by the inner catch.
The second (misleading) exception is of type CommunicationObjectFaultedException and is thrown by channel.Dispose() (?!) called at the end of your using block, hence hides the original one. WCF implementation simply is not following the rule that Dispose() should not throw!
With that being said, the problem is in your client endpoint. According to the service configuration, it should be "baseAddress/StateCheckerService" while currently it's just "baseAddress". So simply use the correct endpoint address
var endpointAddress = new EndpointAddress(
"http://localhost:8000/ServiceModelSamples/Service/StateCheckerService");
and the issue will be solved.
Related
I am new to Azure Service Bus and appreciate any help I can get.
In my current project, using c# and Azure.Messaging.ServiceBus we use an On-Premise server running a "Task Engine" windows service that listens to various queues (including MSMQ) to receive and process messages. We are migrating to Azure Service Bus Queue now.
I implemented ReceiveMessageAsync() method to read and process messages. The connection is persistent because the base class of the Task Engine service is already running in loop. While the below code works fine from my local pc (connected to VPN), it fails with the following error as soon as it's deployed to the on-premise server. The server also uses up all memory and shuts down causing other queues to terminate.
Error messages:
Azure.Messaging.ServiceBus.ServiceBusException: Creation of ReceivingAmqpLink did not complete in 30000 milliseconds. (ServiceTimeout)
Azure.Messaging.ServiceBus.ServiceBusException: 'receiver31' is closed (GeneralError)
Note:
Private Endpoint is enabled on Azure Service Bus and we use token and client credentials to connect to Azure.
All code below works fine locally when run for more than 2 hours and processes messages as soon as they are manually sent to queue using Azure Portal.
Code:
public **override **void StartUp(ContextBase context)
{
// Save the thread context
base.StartUp(context);
//Get values from Config
_tenantId = System.Configuration.ConfigurationManager.AppSettings["tenant-id"];
_clientId = System.Configuration.ConfigurationManager.AppSettings["client-id"];
_clientSecret = System.Configuration.ConfigurationManager.AppSettings["client-secret"];
_servicebusNamespace = System.Configuration.ConfigurationManager.AppSettings["servicebus-namespace"];
_messageQueueName = System.Configuration.ConfigurationManager.AppSettings["servicebus-inbound-queue"];
getAzureServiceBusAccess();
// Set the running flag
_isRunning = true;
}
//Called when service is initialized and then when Reset Connection happens due to error
private static void getAzureServiceBusAccess()
{
var _token = new ClientSecretCredential(_tenantId, _clientId, _clientSecret);
var clientOptions = new ServiceBusClientOptions()
{
TransportType = ServiceBusTransportType.AmqpWebSockets
};
_serviceBusClient = new ServiceBusClient(_servicebusNamespace, _token, clientOptions);
_serviceBusReceiver = _serviceBusClient.CreateReceiver(_messageQueueName, new ServiceBusReceiverOptions());
}
public **override **void DoAction()
{
// Make sure we haven't shut down
if (_isRunning)
{
// Wait next message
tryReceiveMessages();
}
}
private async void tryReceiveMessages()
{
try
{
ServiceBusReceivedMessage message = null;
message = await _serviceBusReceiver.ReceiveMessageAsync();
if (message != null && _isRunning)
{
try
{
string _messageBody = message.Body.ToString();
// <<Send message body to Task Adapter that adds it to the database and processes the job>>
await _serviceBusReceiver.CompleteMessageAsync(message);
}
catch (ServiceBusException s)
{
Tracer.RaiseError(Source.AzureSB, "Azure Service Bus Queue resulted in exception when processing message.", s);
throw;
}
catch (Exception ex)
{
Tracer.RaiseError(Source.AzureSB, "Unexpected error occurred moving task from Azure Service Bus to database; attempting to re-queue message.", ex);
if (message != null)
await _serviceBusReceiver.AbandonMessageAsync(message);
}
}
}
catch (ServiceBusException s)
{
tryResetConnections(s);
}
catch(Exception ex)
{
Tracer.RaiseError(Source.AzureSB, "Azure Service Bus Queue reset connection error.", ex);
throw;
}
}
private void tryResetConnections(Exception exception)
{
try
{
if (DateTime.Now.Subtract(LastQueueReset).TotalSeconds > 1800)
{
LastQueueReset = DateTime.Now;
getAzureServiceBusAccess();
}
else
{
//Send notification email to dev group
}
}
catch (Exception ex)
{
throw;
}
}
private async void closeAndDisposeConnectionAsync()
{
try
{
await _serviceBusReceiver.DisposeAsync();
}
catch(Exception ex)
{
//Do not throw and eat exception - Receiver may have been already disposed
}
try
{
await _serviceBusClient.DisposeAsync();
}
catch (Exception ex)
{
//Do not throw and eat exception - Client may have been already disposed
}
}
We tried to open the network settings on Azure Service Bus to public but that didn't resolve the issue.
I have requested the DevOps team to open ports 443, 5671 and 5672 for AMQPWebSockets and still waiting to hear back to test.
If I use this code for self Host a WCF service in a Console application it works. I run the host app and then from another app (which I call the client app,) I can add the service reference from visual studio > solution explorer > ADD SERVICE REFERENCE > http://10.131.131.14:8080/sendKioskMessage > click GO, add the service with no problems and consume it from the client app (which is a windows form)
But if I run the same code in a Windows Form, I run first the (SELF HOST WCF) windows form app, then from the other app (client app) in visual studio I try to add the service reference from ADD SERVICE REFERENCE in solution explorer (Just the same way that it works before but with the Console App self host) but it throws the following error:
*
An error (Details) occurred while attempting to find services at
http://10.131.131.14:8080/sendKioskMessage.
(If I click Details Link, says the following:)
There was an error downloading
'http://10.131.131.14:8080/sendKioskMessage/$metadata'. Unable to
connect to the remote server. Metadata contains a reference that
cannot be resolved: 'http://10.131.131.14:8080/sendKioskMessage'.
There was no endpoint listening at
http://10.131.131.14:8080/sendKioskMessage that could accept the
message. This is often caused by an incorrect address or SOAP action.
See InnerException, if present, for more details. Unable to connect to
the remote server. If the service is defined in the current solution,
try building the solution and adding the service reference again.
*
The IP that I use is the IP of my pc where both apps are running. I also used localhost instead of my actual IP with the same result.
Windows Form Code (can't add the service from another app):
public partial class KioskosServerForm : Form
{
[ServiceContract]
public interface IKioskMessageService
{
[OperationContract]
string SendKioskMessage(string message);
}
public class KioskMessageService : IKioskMessageService
{
public string SendKioskMessage(string message)
{
return string.Format("Message sent: {0}", message);
}
}
public KioskosServerForm()
{
InitializeComponent();
}
private void KioskosServerForm_Load(object sender, EventArgs e)
{
Uri baseAddress = new Uri("http://10.131.131.14:8080/sendKioskMessage");
try
{
// Create the ServiceHost.
using (ServiceHost host = new ServiceHost(typeof(KioskMessageService), baseAddress))
{
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
// Open the ServiceHost to start listening for messages. Since
// no endpoints are explicitly configured, the runtime will create
// one endpoint per base address for each service contract implemented
// by the service.
host.Open();
}
}
catch (Exception exp)
{
MessageBox.Show(exp.InnerException.Message);
}
}
}
Console App Code (Works! I can add the service from other client app):
[ServiceContract]
public interface IKioskMessageService
{
[OperationContract]
string SendKioskMessage(string message);
}
public class KioskMessageService : IKioskMessageService
{
public string SendKioskMessage(string message)
{
return string.Format("Message sent: {0}", message);
}
}
class Program
{
static void Main(string[] args)
{
Uri baseAddress = new Uri("http://localhost:8080/sendKioskMessage");
// Create the ServiceHost.
using (ServiceHost host = new ServiceHost(typeof(KioskMessageService),baseAddress))
{
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
// Open the ServiceHost to start listening for messages. Since
// no endpoints are explicitly configured, the runtime will create
// one endpoint per base address for each service contract implemented
// by the service.
host.Open();
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine();
// Close the ServiceHost.
host.Close();
}
}
}
I don't know why I can consume the service if the service is self hosted in a console app, but I can't add it if the service is self hosted in a Windows Form.
I will appreciate a lot your help to achieve this from a Windows From, since I need to self host the WCF service from a windows form, no a console app.
I'm using Visual Studio 2017, .Net Framework 4.6.1
THANKS IN ADVANCE GUYS!!
TL;DR the console app works because you have a delay before shutting down the service; the WinForms host doesn't
The reason your console WCF host service works is that you start the hosting and continue until the Console.ReadLine() line:
host.Open();
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine(); // <-------- program waits here
// Close the ServiceHost.
host.Close();
...after which the service is torn down. Prior to that, your other clients can connect fine and add Service References.
The WinForms app has no such delay:
private void KioskosServerForm_Load(object sender, EventArgs e)
{
Uri baseAddress = new Uri("http://10.131.131.14:8080/sendKioskMessage");
try
{
// Create the ServiceHost.
using (ServiceHost host = new ServiceHost(typeof(KioskMessageService), baseAddress))
{
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
// Open the ServiceHost to start listening for messages. Since
// no endpoints are explicitly configured, the runtime will create
// one endpoint per base address for each service contract implemented
// by the service.
host.Open(); // <------ opened here
} // <------ shutdown here
}
catch (Exception exp)
{
MessageBox.Show(exp.InnerException.Message);
}
}
...it is immediately shutdown when the code goes out of scope of the using block. The using will automatically call Dispose() on the host object which in turn calls Close().
Consider placing the host into a variable like so:
ServiceHost _host; // <---------- new!
private void KioskosServerForm_Load(object sender, EventArgs e)
{
Uri baseAddress = new Uri("http://10.131.131.14:8080/sendKioskMessage");
try
{
// Create the ServiceHost.
_host = new ServiceHost(typeof(KioskMessageService), baseAddress))
// Enable metadata publishing.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
_host.Description.Behaviors.Add(smb);
// Open the ServiceHost to start listening for messages. Since
// no endpoints are explicitly configured, the runtime will create
// one endpoint per base address for each service contract implemented
// by the service.
_host.Open();
}
catch (Exception exp)
{
MessageBox.Show(exp.InnerException.Message);
}
}
Later, you can close the _host instance with a call to Close.
I have created functions in WCF and tested them in WCFTestClient.exe. But when testing it on an actual client (mobile). It don't show the functions but the following only.
Example function name is Plus(); it will show PlusAsync(); with void as a return value. Also an event handler (I remember it was PlusEventHandler something).
Please advise me.
You are getting PlusAsync operation because WCF service added in your client as Asynchronously you can also check below image which shows how wcf service get added Asynchronously
But that might be default behaviour with Mobile application Asynchronously and put callback function in that call to get retun value and display it
Example of calling WCF Asynchronous service
private void MakeAsynchronousCall(int NumberOfStudents)
{
BasicHttpBinding basicHttpBinding = new BasicHttpBinding();
StudentService.StudentServiceClient c =
new WFCCallExample.StudentService.StudentServiceClient();
EndpointAddress endpointAddress = c.Endpoint.Address;
StudentService.IStudentService iStudentService =
new ChannelFactory<StudentService.IStudentService>
(basicHttpBinding, endpointAddress).CreateChannel();
AsyncCallback aSyncCallBack =
delegate(IAsyncResult result)
{
try
{
List<StudentService.Student> Students =
iStudentService.EndGetStudents(result);
this.Dispatcher.BeginInvoke((Action)delegate
{ DGStudent.ItemsSource = Students; });
}
catch (Exception ex)
{
this.Dispatcher.BeginInvoke((Action)delegate
{ MessageBox.Show(ex.Message); });
}
};
try
{
iStudentService.BeginGetStudents(NumberOfStudents,
aSyncCallBack, iStudentService);
} catch (Exception ex) { MessageBox.Show(ex.Message); }
}
As explained on How to: Call WCF Service Operations Asynchronously, first you create a callback method which processes the result:
// Asynchronous callbacks for displaying results.
static void AddCallback(object sender, AddCompletedEventArgs e)
{
Console.WriteLine("Add Result: {0}", e.Result);
}
Then you subscribe to the Completed event and issue the call:
client.AddCompleted += new EventHandler<AddCompletedEventArgs>(AddCallback);
client.AddAsync(value1, value2);
As soon as the WCF call returns, your AddCallback will be called.
In a WCF publish/subscribe setup, I currently have an Unsubscribe() method in place to gracefully disconnect clients from the WCF host when the client is closed or needs to stop listening; however, this does not handle cases in which the client aborts forcefully or abnormally, such as the computer itself losing power. If a client application dies in such a way, then its channel remains and the following error is received at the publisher the next time it tries to send out messages:
ExceptionDetail> was caught
The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it has been Aborted.
Clients subscribe anonymously, and the publisher follows a multicasting structure (any subscribed clients/channels should receive the message). Although I am able to catch the exception, I do not know how to single out the faulty channel from this point in the code in order to dispose of it and allow other clients to continue receiving messages. My publishing code looks similar to the following:
public static void Publish(DateTime sendTimeStamp, DataTable sendTable)
{
InstanceContext context = new InstanceContext(null, new PublishStatus());
MessagesClient publishingClient = new MessagesClient(context);
try {
publishingClient.PublishMessage(sendTimeStamp, sendTable);
if (publishingClient.State != CommunicationState.Faulted)
publishingClient.Close();
else
publishingClient.Abort();
}
catch (CommunicationException ex)
{
// This is where the error is caught
}
catch (TimeoutException ex)
{
publishingClient.Abort();
}
catch (Exception ex)
{
publishingClient.Abort();
throw ex;
}
}
Is it possible to isolate the faulty channel from this point (at which the exception first picks up on the issue) and dispose of it so that the publishing service itself can continue to send messages?
After some trial and error as well as exception research, an additional try-catch block in my WCF host was able to unsubscribe incorrectly aborted clients and keep the error from coming back to the publishing service. Posting a simple version here in case someone else stumbles on the same type of problem:
public static event MessageEventHandler MessageEvent;
public delegate void MessageEventHandler(object sender, ServiceEventArgs e);
IClientContract callback = null;
MessageEventHandler messageHandler = null;
public void Subscribe()
{
callback = OperationContext.Current.GetCallbackChannel<IClientContract>();
messageHandler = new MessageEventHandler(Publish_NewMessageEvent);
MessageEvent += messageHandler;
}
public void Unsubscribe()
{
MessageEvent -= messageHandler;
}
public void PublishMessage(DateTime timeStamp, DataTable table)
{
ServiceEventArgs se = new ServiceEventArgs();
se.timeStamp = timeStamp;
se.table = table;
MessageEvent(this, se);
}
public void Publish_NewMessageEvent(object sender, ServiceEventArgs e)
{
try
{
// This callback was causing the error, as the client would no longer exist but the channel would still be open and trying to receive the message
callback.ReceiveMessage(e.timeStamp, e.table);
}
catch
{
// Unsubscribe the dead client.
Unsubscribe();
}
}
I have a WCF windows service which exposes an API to a windows form application.
API connection to client:
var serviceType = typeof(Mail2SmsServerApi);
var uri = new Uri("http://localhost:8000/");
host = new ServiceHost(serviceType, new[] { uri });
var behaviour = new ServiceMetadataBehavior() { HttpGetEnabled = true };
host.Description.Behaviors.Add(behaviour);
host.AddServiceEndpoint(serviceType, new BasicHttpBinding(), "Hello");
host.AddServiceEndpoint(typeof(IMetadataExchange), new BasicHttpBinding(), "mex");
host.Open();
My ServiceContract:
[ServiceContract]
public class Mail2SmsServerApi
{
[OperationContract]
public string Imei()
{
try
{
GSMHandler gsm = new GSMHandler();
return gsm.GetImei();
}
catch (Exception ex)
{
LogText.Error("API GetImei(), exception: " + ex.ToString());
return null;
}
}
}
My GSMHandler class and method:
public bool OpenConnection()
{
modem = new GsmPhone(_comport, _baudrate, _timeout);
if (!comm.IsConnected())
{
try
{
modem.Open();
return true;
}
catch (Exception ex)
{
LogText.Debug("OpenConnection(), exception" + ex.ToString());
return false;
}
}
else
{
try
{
modem.Close();
modem.Open();
return true;
}
catch (Exception ex)
{
LogText.Debug("OpenConnection(), exception" + ex.ToString());
return false;
}
}
}
public string GetImei()
{
string imei = "";
try
{
imei = modem.RequestSerialNumber();
LogText.Debug("IMEI:" + _IMEI);
return imei;
}
catch (Exception ex)
{
LogText.Error("Error caught in GetImei(), exception: " + ex.ToString());
return imei;
}
}
When the OnStart method in my service are called, I'm opening a connection to the modem with :
gsm = new GSMHandler();
gsm.OpenConnection();
When OnStop are called, I'm stopping it with:
gsm = new GSMHandler();
gsm.OpenConnection();
My idea was that, with this design the service would handle the communication and the client and service could interact with the modem without getting a com port not open or com port busy problem. This is obviously wrong, since I'm not able to return values from the modem this way. It has to be a design failure from my side.
What I'm trying to achieve is that a client can talk to the modem through the service, and that not both of them make a direct connection to the modem. But that the service can handle the opening and closing of connection to modem, and pass commands in to the modem from the client...
So my question is, what's the appropriate way to design such a scenario? I'm not asking for the code, just how It's usual to design it...
I'm appreciating all answers :) Thanks in advance!
I think you might have much more success if you performed the interaction with the modem as a single unit of work.
For example, design your service such that the caller calls a single method to send a text message, providing all of the necessary detail in the interface call.
The service method then performs all of the tasks necessary to open the modem, send the text message, and close the modem, in a single unit of work.
This design will allow you to ensure that the modem is always opened and closed correctly and completely within the unit of work instead of waiting for additional commands through the service that may never arrive.
Also, this design will allow you to eventually correctly support multiple modems, which your current design will not. You could have a modem pool and when a new request arrives, you could obtain an available modem from the pool, perform the unit of work, then return the modem to the pool on completion, even in a failure situation.