this is my first approach to WCF and named pipes.
What I have to do is a windows service listening on a named pipe while a small GUI tells it what to do through the pipe.
Everything works well: calls are made to the service, responses are delivered to the GUI and the job gets done.
But if I send two concurrent requests from the GUI then the service will process them one by one: I would like to manually manage concurrency service-side and let it run both requests at the same time.
I've tried creating 2 different pipes for the 2 requests and it does what I need, but of course it's not a solution.
I'm using .NET Framework 4.0 and I can't change it.
Here's my example code:
SERVICE: pipe configuration
ServiceHost host = new ServiceHost(typeof(CommandReceiver), new Uri[] {new Uri("net.pipe://localhost") });
host.AddServiceEndpoint(typeof(ICommandReceiver), new NetNamedPipeBinding(), "myPipe");
host.Open();
SERVICE: Contract interface and implementation
[ServiceContract]
public interface ICommandReceiver
{
[OperationContract]
string Foo();
[OperationContract]
string Bar();
}
public class CommandReceiver : ICommandReceiver
{
public string Foo()
{
//Do stuffs
System.Threading.Thread.Sleep(3000);
return "FOO";
}
public string Bar()
{
//Do stuffs
System.Threading.Thread.Sleep(3000);
return "BAR";
}
}
CLIENT: pipe configuration
ChannelFactory<ICommandReceiver> pipeFactory = new ChannelFactory<ICommandReceiver>(new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/myPipe"));
ICommandReceiver serviceProxy = pipeFactory.CreateChannel();
CLIENT: call to the service
public string GetFoo()
{
return serviceProxy.Foo();
}
public string GetBar()
{
return serviceProxy.Bar();
}
Any advice on how to improve the whole thing, even switching to another communication method, would be really appreciate.
Thank you very much in advance!
OK, I've solved this: first of all I was missing the following attribute on my contract implementation class:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
public class CommandReceiver : ICommandReceiver
{
public string Foo()
{
//Do stuffs
System.Threading.Thread.Sleep(3000);
return "FOO";
}
public string Bar()
{
//Do stuffs
System.Threading.Thread.Sleep(3000);
return "BAR";
}
}
And then I've discovered that the service works perfectly, but not while debugging in visual studio. Releasing and installing it on the system solved the issue.
Related
I have an EXE that I've created called logger which is a simple WinForms application. It has a richtextbox and that's pretty much it.
I then also have a suite of other applications. What I want to be able to do with these apps is to be able to get them to write output to the logger.exe I have full control over the code of all applications.
I understand I could do a process.start and specify arguments but I want these apps to be able to write to the richtextbox at will based on the methods being called within them.
I was hoping I could create an api in the logger.exe that would expose a method for appending the richtextbox.
Does anyone have any tips on how I might achieve this?
EDIT: This is what I have so far:
namespace ScreenLog
{
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, InstanceContextMode = InstanceContextMode.Single)]
public partial class Logger : Form, IFromClientToServerMessages
{
public Logger()
{
InitializeComponent();
}
public void DisplayTextOnServerAsFromThisClient(string text)
{
LogConsole.AppendText(Environment.NewLine + text);
}
}
[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface IFromClientToServerMessages
{
[OperationContract(IsOneWay = false)]
void DisplayTextOnServerAsFromThisClient(string message);
}
}
As you might have already guessed you would need any of IPC(Inter Process Communication) mechanism to send messages between different processes(Applications). WCF is one of the option, You could implement a simple WCF service module which uses net.pipe binding. This service can be hosted in managed application. In your case this service can be hosted in your logger application.
Note: If you want to host a WCF application in a managed application, Particular managed application(Logger) should have admin privilege.
Implementation of Logger Form
partial class declaration
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, InstanceContextMode = InstanceContextMode.Single)]
public partial class Logger: Form, IFromClientToServerMessages
Introduce Interface for communication
This interface should be added to a assembly which is accessible by both Logger application and any other application which sends message to logger.
[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface IFromClientToServerMessages
{
[OperationContract(IsOneWay = false)]
void DisplayTextOnServerAsFromThisClient(string message);
}
Implementing Interface
Add the following method implementation to your Logger form
public void DisplayTextOnServerAsFromThisClient(string text)
{
//Add proper logic to set value to rich text box control.
richtextbox = text;
}
Hosting the WCF service in logger application
Invoke HostTheNetPipeService() within the constructor of Logger Form
private void HostTheNetPipeService()
{
serverHost = new ServiceHost(this);
serverHost.AddServiceEndpoint((typeof(IFromClientToServerMessages)), new NetNamedPipeBinding(), "net.pipe://127.0.0.1/Server");
serverHost.Open();
}
Call the service from other applications to send message/text
private void SendMessageToLogger()
{
using (ChannelFactory<IFromClientToServerMessages> factory = new ChannelFactory<IFromClientToServerMessages>(new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/Server")))
{
IFromClientToServerMessages clientToServerChannel = factory.CreateChannel();
try
{
clientToServerChannel.DisplayTextOnServerAsFromThisClient("Message to be displayed");
}
catch (Exception ex)
{
}
finally
{
CloseChannel((ICommunicationObject)clientToServerChannel);
}
}
}
Closing the communication channel
private void CloseChannel(ICommunicationObject channel)
{
try
{
channel.Close();
}
catch (Exception ex)
{
}
finally
{
channel.Abort();
}
}
I have a desktop app with a duplex WCF service, but I have some troubles using the callback.
The service is started as following in main of program.cs:
ServiceHost svcHost = new ServiceHost(typeof(PeriodicService));
svcHost.Open();
Console.WriteLine("Available Endpoints :\n");
svcHost.Description.Endpoints.ToList().ForEach(endpoint => Console.WriteLine(endpoint.Address.ToString() + " -- " + endpoint.Name));
For the service I created a subscribe function where the callbackchannel is saved in a global variable, then the callback uses that global variable to talk back to the client (there will be only one client connecting).
IPeriodicCallback callbackClient;
public IPeriodicCallback Proxy
{
get
{
return this.callbackClient;
}
}
public void joinPeriodicService()
{
Console.WriteLine("Client subscribe");
this.callbackClient = OperationContext.Current.GetCallbackChannel<IPeriodicCallback>();
}
The thing I want to do now is call the callbackclient from an other class.
In the other class I created the service as:
private PeriodicService periodicService = new PeriodicService();
And I try to write data to it using:
if(this.periodicService.Proxy != null)
{
this.periodicService.Proxy.On1MinuteDataAvailable(tmpPeriod);
}
However the proxy stays null, I also tried to move the proxy part to the class but this also results in it staying null.
When the client connects I nicely get the message "Client Subscribe" but it seems there are two instances running of the periodicservice.
But my problem is I don't see an other way to access the periodicservice then creating it in my class, or is it also already created by the svcHost?
Can ayone point me in the right direction?
This repository shows the a duplex WCF imeplementation I made to answer a similar question a while ago, its a full working example with as little extra stuff as possible.
https://github.com/Aelphaeis/MyWcfDuplexPipeExample
Lets say we have a Service Contract like this :
[ServiceContract(CallbackContract = typeof(IMyServiceCallback),SessionMode = SessionMode.Required)]
public interface IMyService
{
[OperationContract(IsOneWay=true)]
void DoWork();
}
Note that I specified a CallbackContract.
If you want to make a duplex, you would want to perhaps make your Service Behavior implementation of the above contract like this :
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class MyService : IMyService
{
public void DoWork()
{
Console.WriteLine("Hello World");
Callback.WorkComplete();
}
IMyServiceCallback Callback
{
get
{
return OperationContext.Current.GetCallbackChannel<IMyServiceCallback>();
}
}
}
The important thing here is the Callback. This is how your service would allow you to access specified to you by the Client.
You also need to define the callback interface, In my case its quite simple :
[ServiceContract]
public interface IMyServiceCallback
{
[OperationContract(IsOneWay = true)]
void WorkComplete();
}
Now I want to create a client to use this Duplex Service. The first thing I need to do is implement the IMyServiceCallback. I need to do this on the client. In this case the implementation is this:
class Callback : IMyServiceCallback
{
public void WorkComplete()
{
Console.WriteLine("Work Complete");
}
}
Now when I want to open my duplex connection with the services I would create a proxy class like this something like this:
public class MyServiceClient: IMyService, IDisposable
{
DuplexChannelFactory<IMyService> myServiceFactory { get; set; }
public MyServiceClient(IMyServiceCallback Callback)
{
InstanceContext site = new InstanceContext(Callback);
NetNamedPipeBinding binding = new NetNamedPipeBinding();
EndpointAddress endpointAddress = new EndpointAddress(Constants.myPipeService + #"/" + Constants.myPipeServiceName);
myServiceFactory = new DuplexChannelFactory<IMyService>(site, binding, endpointAddress);
}
public void DoWork()
{
myServiceFactory.CreateChannel().DoWork();
}
public void Dispose()
{
myServiceFactory.Close();
}
}
Notice that I specified an InstanceContext. That Instance Context will be an instance of the object I created that implements IMyServiceCallback.
That's all you need to do! Simple as that!
Update :
Callback objects are just like any other object. You can store them into a collection and iterate through them and based on some condition.
One way is to create a property in the IMyServiceCallback that can uniquely identify it. When a client connects to the service it can call a method which specifies a callback object which can then be cached or saved for later use. You can then iterate the callbacks and based on some condition you can call a method for a specific client.
This is certainly more complicated; however, it is certainly manageable. I will add an example in a bit.
Update 2
This is a working example of exactly what you want; however, its a lot more complicated. I'll try to explain as simply as I can : https://github.com/Aelphaeis/MyWcfDuplexPipeExample/tree/MultiClient
Here is a list of the changes:
I've modified the client proxy (and service) so that when initialized it calls the init Method
I've also modified the Service implementation so that now it is a single instance dealing with all requests (for convenience).
I added a new OperationContract in the Service interface called Msg
I've added a new Method in the IMyServiceCallback called RecieveMessage.
I've added a way to identify the client.
In the proxy class I have the following :
public MyServiceClient(IMyServiceCallback Callback)
{
InstanceContext site = new InstanceContext(Callback);
NetNamedPipeBinding binding = new NetNamedPipeBinding();
EndpointAddress endpointAddress = new EndpointAddress(Constants.myPipeService + #"/" + Constants.myPipeServiceName);
myServiceFactory = new DuplexChannelFactory<IMyService>(site, binding, endpointAddress);
Init();
}
public void Init()
{
myServiceFactory.CreateChannel().Init();
}
In my service I have the following :
public class MyService : IMyService
{
public List<IMyServiceCallback> Callbacks { get; private set; }
public MyService(){
Callbacks = new List<IMyServiceCallback>();
}
public void Init()
{
Callbacks.Add(Callback);
}
// and so on
My IMyServiceCallback has been redefined to :
[ServiceContract]
public interface IMyServiceCallback
{
[OperationContract]
int GetClientId();
[OperationContract(IsOneWay = true)]
void WorkComplete();
[OperationContract(IsOneWay = true)]
void RecieveMessage(String msg);
}
By specifying a number, you can contact the client that corresponds with that number. If two clients have the same Id, both clients will be contacted.
I'm looking for a way to cache objects in memory with a RESTful WCF service. The service is completely stateless and is hosted outside of an IIS. I want to implement the caching by myself, so memcached isn't an option.
Right now I'm thinking of hosting a separate stateful System.ServiceModel.ServiceHost that does all the caching. It'll communicate with the rest of the WCF methods through a separate port or by some other means. However I'm not sure if this is the ideal solution to my problem. Has anyone got any tips?
I understand your confusion between stateless service and a stateful host and how the two can interact.
In this code sample I demonstrate conceptually how an in-memory singleton (Caching mechanism, I refer to as CachingProvider henceforth) can be referenced by both the service class (the service instance more precisely during the lifecycle of the request) and the service host (in this case I chose it to be a Console Application)
I assume here, the service interface and class are both located within the console applicaiton project that hosts the service.
In this simple example, my primitive CachingProvider class basically acts as a counter of how many service calls are made to the GetData method, and the service host will poll the CachingProvider every 5 seconds to get the count of service calls made so far.
note: you can use the WCFTestClient utility to test this quickly.
Disclaimer: I by no means suggest that a complex Caching mechanism be implemented as simply as in this sample, this code is merely for demosntration purposes.
namespace ServiceHostConsole
{
[ServiceContract]
public interface ITestService
{
[OperationContract]
string GetData(int value);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class TestService : ITestService
{
public TestService()
{
CachingProvider.CallCount++;
}
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
}
//For demonstration purposes only
static class CachingProvider
{
static CachingProvider()
{
//private static constructor can initialize
//static cacheable resources
_callCounter = 0; //Trivial example of initialization
}
private static int _callCounter;
public static int CallCount
{
set { _callCounter = value; }
get { return _callCounter; }
}
}
class Program
{
static void Main()
{
using (var host = new ServiceHost(typeof(TestService), new Uri("http://localhost/TestService")))
{
host.Open();
//Example how the ServiceHost can report on a persistent in-memory object that is being
//updated each time the service is called.
new Timer(state => Console.WriteLine("# of service calls: {0}", CachingProvider.CallCount), null, 0, 5000);
Console.Read();
host.Close();
}
}
}
}
I am very new to WCF and I am trying to learn but I think I am missing something significant here and I am aware of that so please be kind. I am working with a pre-existing console application that I have added a WCF host to, this is an oversimplified version of it but it should give you the jist of it
namespace mynamespace
{
public class MyConsoleApp
{
static void Main(string[] args)
{
CreateRemoteDebugHost();
StartLongRunningMethods();
}
public static void StartLongRunningMethods()
{
LongRunningMethod1();
LongRunningMethod2();
}
public static void LongRunningMethod1()
{}
public static void LongRunningMethod2()
{}
public void CreateRemoteDebugHost()
{
ServiceHost host = new ServiceHost(typeof(RemoteDebug), new Uri("net.pipe://localhost"));
host.AddServiceEndpoint(typeof(IRemoteDebug), new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), "PipeRemoteDebug");
//Create mex
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.HttpGetUrl = new Uri("http://localhost:8001/RemoteDebug");
host.Description.Behaviors.Add(smb);
host.Open();
}
}
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IRemoteDebugCallback))]
public interface IRemoteDebug
{
[OperationContract]
string Message(string message);
}
public interface IRemoteDebugCallback
{
[OperationContract(IsOneWay = true)]
void OnMessage(string callbackValue);
}
public class RemoteDebug : IRemoteDebug
{
public string Message(string message)
{
IRemoteDebugCallback callback = OperationContext.Current.GetCallbackChannel<IRemoteDebugCallback>();
callback.OnMessage(message);
return message;
}
}
}
As you can probably tell I am trying to send debug or status messages back to a client(s) from inside of long running static methods. All the plumbing seems to be working correctly, the host comes up, I can add a service reference to my client application just fine but the trouble starts when try to invoke the WCF callback from the longrunningprocesses static methods. I can't seem to figure out how to do that properly.
What is also very confusing is that almost every example I have seen of WCF and callbacks assumes that everything you are doing is running from within the context of the WCF host itself, obviously in my example this is not the case. I know I'm probably going aobut this all wrong so could someone please set me straight on this? Any help is greatly appreciated.
TIA!
There is client (not to be confused with the client program) created as well through app.config or manually (e.g. public class MyClient: ClientBase<IRemoteDebug> or public class MyClient: DuplexClientBase<IRemoteDebug>, IRemoteDebug). This should send messages to the client programs. Example using DuplexClient above from some code I had:
[CallbackBehaviorAttribute(UseSynchronizationContext = true)]
public class SubCallback : IRemoteDebug
{
public void Event(SomeClass evt)
{
// some handling code using:
//public delegate void EventCallbackHandler(SomeClass evt);
}
}
InstanceContext ctx = new InstanceContext(new SubCallback ());
MyClient _client = new MyClient(
ctx,
new NetNamedPipeBinding(NetNamedPipeSecurityMode.None),
new EndpointAddress("net.pipe://localhost/ServiceEndpointName"));
Also, you may want to pass some options to your service, such as:
[ServiceBehavior(
InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Single)]
public class RemoteDebug : IRemoteDebug
{}
It could be many things causing your particular issue, but this solved problems for me.
Context:
I need to develop a monitoring server that monitors some of our applications (these applications are in c#). So I decided to develop the system with WCF which seems suitable for my needs.
These applications must register themselves to the monitoring server when they start. After that the monitoring server can call the methods Start or Stop of these applications.
Everything is completely executed on the same machine, nothing needs to be executed remotely.
So I developed a good prototype and everything works fine. Each application registers itself to the monitoring server.
Question:
ApplicationRegistrationService (see the code below) is the implementation of the monitoring service and it is a singleton instance due to the ServiceBehavior attribute.
Here my problem: I want to access the content of ApplicationRegistrationService per example, the number of connected applications from my server (ConsoleMonitoringServer in the example). But, I am not sure how to achieve this.
Do I need to create a channel in my server to the service like I did in my clients (ConsoleClient) or it exists a better way to achieve this?
Code:
The code is very simplified for the purpose of this question:
//The callback contract interface
public interface IApplicationAction
{
[OperationContract(IsOneWay = true)]
void Stop();
[OperationContract(IsOneWay = true)]
void Start();
}
[ServiceContract(SessionMode = SessionMode.Required,
CallbackContract = typeof(IApplicationAction))]
public interface IApplicationRegistration
{
[OperationContract]
void Register(Guid guid, string name);
[OperationContract]
void Unregister(Guid guid);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Multiple)]
public class ApplicationRegistrationService : IApplicationRegistration
{
//IApplicationRegistration Implementation
}
public class ApplicationAction : IApplicationAction
{
//IApplicationAction Implementation
}
Console application for this example
class ConsoleClient
{
static void Main(string[] args)
{
ApplicationAction actions = new ApplicationAction();
DuplexChannelFactory<IApplicationRegistration> appRegPipeFactory =
new DuplexChannelFactory<IApplicationRegistration>(actions,
new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/AppReg"));
IApplicationRegistration proxy = appRegPipeFactory.CreateChannel();
proxy.Register(Guid.Empty, "ThisClientName");
//Do stuffs
}
}
Console server for this example
class ConsoleMonitoringServer
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(ApplicationRegistrationService),
new Uri[]{ new Uri("net.pipe://localhost")}))
{
host.AddServiceEndpoint(typeof(IApplicationRegistration),
new NetNamedPipeBinding(), "AppReg");
host.Open();
//Wait until some write something in the console
Console.ReadLine();
host.Close();
}
}
}
Finally, I find the answer and it was pretty easy. I just need to create the service instance and pass the reference to the constructor of ServiceHost.
So I need to replace the following code:
using (ServiceHost host = new ServiceHost(typeof(ApplicationRegistrationService),
new Uri[]{ new Uri("net.pipe://localhost")}))
by :
ApplicationRegistrationService myService = new ApplicationRegistrationService();
using (ServiceHost host = new ServiceHost(myService,
new Uri[]{ new Uri("net.pipe://localhost")}))
If you mean you'd like two way communication between your monitoring service and your registered services or nodes, then you probably should be using two way communication in WCF also known as duplex communication. Very cool stuff.
http://www.codeproject.com/KB/WCF/WCF_Duplex_UI_Threads.aspx