How can I throttle a WCF MSMQ endpoint? - wcf

I'm new to WCF and just learning how to get a client to talk to a host (both in console applications) using MSMQ.
I want to be able to send messages from client to host and have the host pick them up immediately or, if the host is stopped, to continue where it left off when it is restarted.
I've got this almost working but I find that when I restart the host with ten messages in the queue, the messages are not processed in the queue order. I assume there's some multithreading going on that makes them appear out of order. I'd like to be able to limit the WCF service to processing one message at a time to stop this happening (unless there's a better solution).
It's essential to a system that I'm about to work on that the MSMQ messages are processed in order and not in parallel.
The code for my service contract is:
[ServiceContract(Namespace = "http://www.heronfoods.com/DemoService")]
public interface IDemoService
{
[OperationContract(IsOneWay = true)]
void SendMessage(string message);
}
For the Service contract implementation I've got this. (The console output is because this is a demo app for me to learn from):
public class DemoService : IDemoService
{
public void SendMessage(string message)
{
Console.WriteLine("{0} : {1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), message);
}
}
My host application is a console application with the following code:
class Program
{
static void Main(string[] args)
{
Console.Title = "WCF Host";
using (var host = new ServiceHost(typeof(Library.DemoService)))
{
var endpoint = new ServiceEndpoint(
ContractDescription.GetContract(typeof(Library.IDemoService)),
new NetMsmqBinding(NetMsmqSecurityMode.None),
new EndpointAddress("net.msmq://localhost/private/test"));
host.AddServiceEndpoint(endpoint);
host.Open();
Console.WriteLine("Host Active");
Console.ReadLine();
}
}
}
The client is equally simple:
class Program
{
static void Main(string[] args)
{
Console.Title = "WCF Client";
IDemoService proxy = ChannelFactory<IDemoService>.CreateChannel(
new NetMsmqBinding(NetMsmqSecurityMode.None),
new EndpointAddress("net.msmq://localhost/private/test")
);
do
{
string msg = Console.ReadLine();
if (msg=="")
break;
else
proxy.SendMessage(msg);
} while (true);
}
}

I am assuming your queue is not transactional.
While I'm not certain there's a way to throttle netMsmqBinding to a single thread, you shouldn't need to apply this restriction.
To guarantee ordered delivery you only need to make your queue transactional and then apply the exactlyOnce attribute to the netMsmqBinding configuration.
See example here.

Related

How to add SoapEndPoint after server is started on ASP.NET Core?

I'm using simple console app to expose a soap web service. It works as expected.
Now i want to add another web service after the server is started. How to make it work?
I have following simple console application:
static void Main(string[] args)
{
var host = WebApplication.CreateBuilder();
_App = host.Build();
_App.UseRouting();
_App.UseEndpoints(endpoints =>
{
endpoints.UseSoapEndpoint<ISimpleServiceInterface>("/SimpleService.asmx", new SoapEncoderOptions(), SoapSerializer.XmlSerializer);
});
_App.Urls.Add("http://*:5000");
_App.RunAsync();
Console.WriteLine("Server has been started successfully ...");
AddNewService();
Console.ReadLine();
}
Server starts and i can access the wsdl http://localhost:5000/SimpleService.asmx?wsdl)
Now the AddNewService method simple try to define a new SoapEndPoint after service started.
Code looks like this:
static private void AddNewService()
{
try
{
System.Threading.Thread.Sleep(5000); // Wait 5 seconds to make sure web application is running
Console.WriteLine("Adding new service ..."); // Add new Soap service now, after startup
_App?.UseEndpoints(endpoints =>
{
endpoints.UseSoapEndpoint<ISimpleServiceInterface2>("/SimpleService2.asmx", new SoapEncoderOptions(), SoapSerializer.XmlSerializer);
});
Console.WriteLine("Added new service.");
}
catch(Exception ex)
{
Console.WriteLine("Failed to Add new service. Error=" + ex.Message);
}
}
This works ok if first request to url is done after the service is created: (http://localhost:5000/SimpleService2.asmx?wsdl)
But if a request is sent before the service is created. Then any request done after the creation of the service will fail:
I'm guessing i need to raise some event or something to the web server to get it refreshed or something.
How can i do that?
Also is there a way to remove a SoapEndPoint once is has been defined/exposed?
Idea is basically being able to add/remove/update SoapEndPoint on the fly.
Any help will be appreciated. Thanks in advance
Progess a little bit on this.
I basically need to register IActionDescriptorChangeProvider class to be able to notify the web application.
I also needed to slightly change my main routine.
Here is the main function:
static void Main(string[] args)
{
var host = WebApplication.CreateBuilder();
host.Services.AddControllers();
host.Services.AddSingleton<IActionDescriptorChangeProvider>(MyActionDescriptorChangeProvider.Instance);
host.Services.AddSingleton(MyActionDescriptorChangeProvider.Instance);
host.Services.AddSingleton<ISimpleServiceInterface, SimpleService>();
_App = host.Build();
_App.MapControllers();
_App.UseRouting();
_App.UseEndpoints(endpoints =>
{
endpoints.UseSoapEndpoint<ISimpleServiceInterface>("/SimpleService.asmx", new SoapEncoderOptions(), SoapSerializer.XmlSerializer);
});
_App.Urls.Add("http://*:5000");
_App.RunAsync();
Console.WriteLine("Server has been started successfully ...");
AddNewService();
Console.ReadLine();
}
Then the AddService function (note the 2 lines added to make the notification):
static private void AddNewService()
{
try
{
System.Threading.Thread.Sleep(5000); // Wait 5 seconds to make sure web application is running
Console.WriteLine("Adding new service ..."); // Add new Soap service now, after startup
_App?.UseEndpoints(endpoints =>
{
endpoints.UseSoapEndpoint<ISimpleServiceInterface2>("/SimpleService2.asmx", new SoapEncoderOptions(), SoapSerializer.XmlSerializer);
});
// Notify the web application of the changes
MyActionDescriptorChangeProvider.Instance.HasChanged = true;
MyActionDescriptorChangeProvider.Instance.TokenSource.Cancel();
Console.WriteLine("Added new service.");
}
catch(Exception ex)
{
Console.WriteLine("Failed to Add new service. Error=" + ex.Message);
}
}
and class implementing IActionDescriptorChangeProvider:
public class MyActionDescriptorChangeProvider : IActionDescriptorChangeProvider
{
public static MyActionDescriptorChangeProvider Instance { get; } = new MyActionDescriptorChangeProvider();
public CancellationTokenSource TokenSource { get; private set; } = new CancellationTokenSource();
public bool HasChanged { get; set; }
public IChangeToken GetChangeToken()
{
TokenSource = new CancellationTokenSource();
return new CancellationChangeToken(TokenSource.Token);
}
}
Once you do that, it will work fine on the second request (wsdl request).
Problem is that the wsdl may be accessible but the function itself (route to the actual method on the singleton) is not there.
Registration of the singleton for ISimpleServiceInterface2 need to be done but not sure how to achieve this.
My end goal is to be able to add/remove/update soap web service after server is built.
Basically idea is to update the soap service with a newer assembly.
If anybody has some idea, comments, response, please post them here. That will be appreciated.

WCF oneway exception faults channel

I haven't found a clear answer on this. so if there is already a question about this, my bad.
I have a WCF service that pushes data via a callback method to connected clients. this callback method is oneway. so everytime there is new data I loop over the connected users and push the data.
The problem I have right now is when a client disconnects it throws an error and the channel becomes faulted.
I always thought that oneway didn't care if the message arrives at the destination. So if there's no client, then bad luck. but no exception.
but there is an exception and that exception faults the channel.
Now I've read somewhere that if you enable reliable sessions, that the exception won't fault the channel. Is this true?
How can I prevent that the channel goes into faulted state when an exception happens on a oneway call?
The list of registered and avaiable clients you can store in some resource such as List. Create another interface which exposes Connect/Disconnect methods. Connect is invoked when application starts off and within method client is added to the list. Disconnect in turn is invoked when application shuts down in order to get rid client of list. OnStartup/OnClosing events or their equivalents, depending on what kind of application client is, refer to moment when application is launched and closed. Such a solution ensures that resource stores only users avaiable to be reached.
[ServiceContract]
interface IConnection
{
[OperationContract(IsOneWay = true)]
void Connect();
[OperationContract(IsOneWay = true)]
void Disconnect();
}
[ServiceContract]
interface IServiceCallback
{
[OperationContract(IsOneWay = true)]
void CallbackMethod();
}
[ServiceContract(CallbackContract = typeof(IServiceCallback))]
interface IService
{
[OperationContract]
void DoSth();
}
class YourService : IConnection, IService
{
private static readonly List<IServiceCallback> Clients = new List<IServiceCallback>();
public void Connect()
{
var newClient = OperationContext.Current.GetCallbackChannel<IServiceCallback>();
if (Clients.All(client => client != newClient))
Clients.Add(newClient);
}
public void Disconnect()
{
var client = OperationContext.Current.GetCallbackChannel<IServiceCallback>();
if (Clients.Any(cl => cl == client))
Clients.Remove(client);
}
public void DoSth()
{
foreach(var client in Clients)
client.CallbackMethod();
}
}
At the end expose another endpoint with IConnection so that client can create proxy meant to be used only for connection/disconnection.
EDIT:
I know it has been a while since I posted an answear but I did not find in order to prepare an example. The workaround is to let service's interface derive IConnection and then expose only service as an endpoint. I attach simple example of WCF and WPF app as client. Client's application violates MVVM pattern but in this case it is irrelevant. Download it here.
To add on what Maximus said.
I've implemented this pattern in a class where clients can subscribe to get updates of internal states of a system, so a monitoring client can show graphs and other clients do other stuff like enabling/disabling buttons if some state is active.
It removes faulted channels from the list when they fail. Also all current states are sent when a client connects.
here's the code, hope it helps!
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Publish : IPublish
{
private struct SystemState
{
public string State;
public string ExtraInfo;
}
private static Dictionary<Key<string>, IPublishCallback> mCallbacks = new Dictionary<Key<string>, IPublishCallback>();
private static Dictionary<string, SystemState> mStates = new Dictionary<string, SystemState>();
public void RegisterClient(string name, string system)
{
lock (mCallbacks)
{
IPublishCallback callback = OperationContext.Current.GetCallbackChannel<IPublishCallback>();
Key<string> key = new Key<string>(name, system);
if (!mCallbacks.ContainsKey(key))
{
mCallbacks.Add(key, callback);
}
else
{
mCallbacks[key] = callback;
}
foreach (KeyValuePair<string, SystemState> s in mStates)
{
mCallbacks[key].ServiceCallback(s.Key, s.Value.State, s.Value.ExtraInfo);
}
}
}
public void UnregisterClient(string name)
{
lock (mCallbacks)
{
outer: foreach (var key in mCallbacks.Keys)
{
if (key.Key1 == name)
{
mCallbacks.Remove(key);
goto outer;
}
}
}
}
public void SetState(string system, string state, string extraInfo)
{
lock (mCallbacks)
{
List<Key<string>> toRemove = new List<Key<string>>();
SystemState s = new SystemState() { State = state, ExtraInfo = extraInfo };
SystemState systemState;
if (!mStates.TryGetValue(system, out systemState))
mStates.Add(system, s);
else
mStates[system] = s;
foreach (KeyValuePair<Key<string>, IPublishCallback> callback in mCallbacks)
{
try
{
callback.Value.ServiceCallback(system, state, extraInfo);
}
catch (CommunicationException ex)
{
toRemove.Add(new Key<string>(callback.Key.Key1, callback.Key.Key2));
}
catch
{
toRemove.Add(new Key<string>(callback.Key.Key1, callback.Key.Key2));
}
}
foreach (Key<string> key in toRemove)
mCallbacks.Remove(key);
}
}
}

Closing WCF Connections from service

I have recently started a new job where WCF services are being used. I have used them in the past and am comfortable with them but from what I can recall if the client does not close the connection it has the ability to bring your service down entirely. I am aware of the proper procedure for closing the connections but if the responsibility is on the client, they may not follow the same practices and potentially have the ability to bring the service down. Is there any other way of handling the closing of the connections so that it is not reliant on the client doing the right thing? It seems odd that anyone who has access to your service has the ability to bring it down with such ease...
Thank you very much for any insights!
One option is to use session time out in the server. This actually faults the client channel.
There are only really three ways in which a session can terminated:
1) The client closes the proxy
2) The service's receiveTimeout is exceeded before the client sends another request
3) The service throws a non-fault exception which will fault the channel and so terminate the session
If you don't want the client involved then you only have 2 and 3 neither of which end well for the client - they will get an exception in both situation on the next attempt to talk to the service.
You could use Duplex messaging and get the service to notify the client that its requires session termination - the client then gets an opportunity to close down the proxy gracefully but this is a cooperative strategy
Or you need to use duplex (but still the client will have to call the service).
Here is some important points of the service implementation:
a: Use a static dictionary to keep the Client’s IP and callback channel. Before writing on the share object, lock the object.
b: Gets the IP address of the client using the GetAddressAsString method. You can get the IP of the client from the incoming message. The following statement shows how can we get the IP adddress of the Client in WCF:
RemoteEndpointMessageProperty clientEndpoint = OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
String ipAddress = clientEndpoint.Address;
If you are using the namepipe binding, you will not get the RemoteEndpointMessageProperty.
c: When the client creates the proxy of the service, it will call StartingService method immediately. Inside the StartingService method, I am keeping the callback channel of the client and current instance into the dictionary.
d: When the user of WCF service wants to disconnect a client, he/she will call the Disconnect method with the IP Address of the client.
e: The Disconnect method uses the IP Address to get the callback channel of the client and associate service instance of the client from the dictionary. Eventually, it notifies the client by using callback channel and close the incoming channel.
Here is the implementation through code:
[ServiceContract(CallbackContract=typeof(INotifyClientCallback),SessionMode=SessionMode.Required)]
public interface IService1
{
[OperationContract]
bool StartingService();
}
public interface INotifyClientCallback
{
[OperationContract(IsOneWay = true)]
void Disconnecting();
}
INotifyClientCallback interface for Callback.
Step 2: Implementation of the Contact:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class Service1 : IService1
{
private static readonly Dictionary subscribers = new Dictionary();
public static event EventHandler onClientAdded;
///
/// Returns the IP Address of the Client
///
///
public string GetAddressAsString()
{
if (!OperationContext.Current.IncomingMessageProperties.ContainsKey(RemoteEndpointMessageProperty.Name))
{
return "127.0.0.1";
}
RemoteEndpointMessageProperty clientEndpoint =
OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
return clientEndpoint.Address;
}
public bool StartingService()
{
//Get the callback reference
INotifyClientCallback callback = OperationContext.Current.GetCallbackChannel();
string IPAddress = GetAddressAsString();
lock (subscribers)
{
if (!subscribers.ContainsKey(IPAddress))
{
subscribers[IPAddress] = new CommunicationStore()
{ NotifyCallback = callback,
IService = OperationContext.Current.InstanceContext
};
if (onClientAdded != null)
{
onClientAdded(IPAddress, null);
}
}
}
return true;
}
public static void Disconnect(string ipAddress)
{
if (subscribers.ContainsKey(ipAddress))
{
CommunicationStore com = subscribers[ipAddress];
if (((ICommunicationObject)com.NotifyCallback).State == CommunicationState.Opened)
{
try
{
//fires the callback method
com.NotifyCallback.Disconnecting();
com.IService.IncomingChannels.FirstOrDefault().Close();
}
catch (Exception)
{
throw;
}
}
}
}
}
public class CommunicationStore
{
public InstanceContext IService { get; set; }
public INotifyClientCallback NotifyCallback { get; set; }
}

Why does the following code return an error when consuming multiple endpoints of a WCF service?

I have created a multi-endpoint WCF service and consumed and it is working fine.
But when I am trying to close the service client then am getting error.
This is how I am creating the client object and disposing its working fine for single endpoint WCF service
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
ICardPrintingService Service = null;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Service = new CardPrintingServiceClient();
var response = this.Service.GetCardData(new GetCardDataRequest { NIK = 6666620501740003 });
try
{
((CardPrintingServiceClient)Service).Close();
}
catch (Exception ex)
{
MessageBox.Show("error");
}
}
}
}
This is going to the catch block when closing the connection with error message
The remote endpoint no longer recognizes this sequence. This is most
likely due to an abort on the remote endpoint. The value of
wsrm:Identifier is not a known Sequence identifier. The reliable
session was faulted.
Can some one tell me why?
Thanks a ton in adv
Raghavendra
What is the need of casting overhere.
((CardPrintingServiceClient)Service).Close(); //pls explain this.
you can try this in finally block.
if (Service.State != System.ServiceModel.CommunicationState.Closed)
{
Service.Abort();
}

How do I solve the error in my WCF service and proceed on?

I am currently developing a C# Windows Form Application that I intend to let it interact with a server. The server will receive posting from a mobile application that I have developed and whenever a posting is received, my Windows Form Application should be notified and give me a notification.
E.g. My mobile application sends an message over to my server. Once my server receives the message, my windows form application should display a new notification showing the content of the message received.
I am now starting to develop my WCF Service and this is what I've done so far
[ServiceContract(Namespace = "Posting")]
public interface IPostingService
{
[OperationContract]
void NotifyAboutPosting(Posting post);
}
[DataContract]
public class Posting
{
[DataMember]
public int ID { get; set; }
[DataMember]
public string Description { get; set; }
[DataMember]
public DateTime PostingTimestamp { get; set; }
}
class Program
{
static void Main(string[] args)
{
Uri baseAddress = new Uri("http://localhost/");
ServiceHost selfHost = new ServiceHost(typeof(Posting), baseAddress);
try
{
// Step 3 of the hosting procedure: Add a service endpoint.
selfHost.AddServiceEndpoint(
typeof(IPostingService),
new WSHttpBinding(),
"Posting");
// Step 4 of the hosting procedure: Enable metadata exchange.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
selfHost.Description.Behaviors.Add(smb);
// Step 5 of the hosting procedure: Start (and then stop) the service.
selfHost.Open();
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();
Console.ReadLine();
// Close the ServiceHostBase to shutdown the service.
selfHost.Close();
}
catch (CommunicationException ce)
{
Console.WriteLine("An exception occurred: {0}", ce.Message);
selfHost.Abort();
}
}
}
regarding the posting class, what I want to ask is that are the methods inside used to get the information from the server?
and how do I proceed on from here after the service is done. (My winform application has been finished and all thats left is to add in this logic to receive the posting whenever the mobile app sends to the server.
and there seems to be a compilation error of
The contract name '##.IPostingService' could not be found in the list of contracts implemented by the service '##.Posting'.
could anyone help me with this? thanks a million!
Where is your actual implementation? You have contract (IPostingService), data (Posting)... but where's code doing the work? You seem to lack contract implementation:
public class PostingService : IPostingService
{
public void NotifyAboutPosting(Posting post)
{
// do something with post here
}
}
And you register actual worker class (not data) when setting up your host:
ServiceHost selfHost = new ServiceHost(typeof(PostingService), baseAddress);
I would suggest checking out these likely culprits:
WCF - Contract Name could not be found in the list of contracts
Discussion on Microsoft forums
Blog post on the issue