Notify the Multiple instances of UI using Wcf service - c#

I have a application in WPF, which will allow me to add,delete and edit student. That UI can be opened more than once.
When the UI makes change to the data through the service every other connected client should also be updated with latest changes.
Is that possible to have wcf service do it for me? How can we do it?

Each WPF UI window should establish a connection with the host WCF Service.
The Service is required to be a of singleton type.
Also you'll have to enable session.
Each UI window should start have it's own connection with the service. And must also handle callback method.
The service must track these session and callback method ID.
Now when a UI thread makes change to the data (I am assuming using the WCF service in consideration) the service will have to iterate the session collection and send notification.
There are only two binding that support this netTcp and WSDualHttp.
The Service and Callback service would look as below:
[ServiceContract(SessionMode = SessionMode.Required,
CallbackContract = typeof(INotifyMeDataUpdate))]
public interface IService
{
[OperationContract(IsInitiating=true)]
void Register();
[OperationContract(IsTerminating= true)]
void Unregister();
[OperationContract(IsOneWay=true)]
void Message(string theMessage);
}
public interface INotifyMeDataUpdate
{
[OperationContract(IsOneWay=true)]
void GetUpdateNotification(string updatedData);
}
The implementation would as below:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class Service : IService
{
object _lock = new object();
Dictionary<string, INotifyMeDataUpdate> _UiThreads =
new Dictionary<string, INotifyMeDataUpdate>();
public void Register()
{
string id = OperationContext.Current.SessionId;
if (_UiThreads.ContainsKey(id)) _UiThreads.Remove(id);
_UiThreads.Add(id, OperationContext.Current.GetCallbackChannel<INotifyMeDataUpdate>());
}
public void Unregister()
{
string id = OperationContext.Current.SessionId;
if (_UiThreads.ContainsKey(id)) _UiThreads.Remove(id);
}
public void Message(string theMessage)
{
foreach (var key in _UiThreads.Keys)
{
INotifyMeDataUpdate registeredClient = _UiThreads[key];
registeredClient.GetUpdateNotification(theMessage);
}
}
}

Related

Communication between Topshelf service (acting as TCP server) and selfhosted OWIN WebAPI

I have a Topshelf windows service that acts as a TCP server. Inside this service, I also have a self-hosted (OWIN) WebAPI.
My goal is to somehow allow the WebAPI to communicate with the TCP server that's contained and running in the same service. Naturally I could simply use something like a "trigger" file or a shared DB that could be polled frequently, though I'd like to know of any more optimal/native ways to achieve this.
To get a better idea of the project, think of a single page application consuming my API and making certain calls with arbitrary string parameters. This data should then be passed to clients (C++ console apps using winsock) that are connected to the running TCP server.
The following Container is instantiated and passed to the Topshelf HostConfigurator
class ContainerService
{
private APIService _apiService;
private EngineService _engineService;
protected IDisposable WebAppHolder { get; set; }
public bool Start(HostControl hostControl)
{
var host = hostControl;
_apiService = new APIService();
_engineService = new EngineService();
// Initialize API service
if (WebAppHolder == null)
{
WebAppHolder = _apiService.Initialize();
}
// Initialize Engine service
_engineService.Initialize();
return true;
}
public bool Stop(HostControl hostControl)
{
// Stop API service
if (WebAppHolder != null)
{
WebAppHolder.Dispose();
WebAppHolder = null;
}
// Stop Engine service
_engineService.Stop();
return true;
}
}
Standard Topshelf stuff in program entry point (as mentioned above):
HostFactory.Run(hostConfigurator =>
{
hostConfigurator.Service<ContainerService>(containerService =>
{
containerService.WhenStarted((service, control) => service.Start(control));
containerService.WhenStopped((service, control) => service.Stop(control));
});
hostConfigurator.RunAsLocalSystem();
hostConfigurator.SetServiceName("Educe Service Host");
hostConfigurator.SetDisplayName("Communication Service");
hostConfigurator.SetDescription("Responsible for API and Engine services");
});
TCP Server:
public void Initialize()
{
_serverListener = new TcpListener(new IPEndPoint(hostAddress, (int)port));
_serverListener.Start();
_threadDoBeginAcceptTcpClient = new Thread(() => DoBeginAcceptTcpClient(_serverListener));
_threadDoBeginAcceptTcpClient.Start();
}
...
public void DoBeginAcceptTcpClient(TcpListener listener)
{
while(!_breakThread)
{
// Set the event to nonsignaled state.
TcpClientConnected.Reset();
// Start to listen for connections from a client.
Console.WriteLine("Waiting for a connection...");
// Accept the connection.
listener.BeginAcceptTcpClient(DoAcceptTcpClientCallback, listener);
// Wait until a connection is made and processed before continuing.
TcpClientConnected.WaitOne();
}
}
// Process the client connection.
public void DoAcceptTcpClientCallback(IAsyncResult ar)
{
// Get the listener that handles the client request.
TcpListener listener = (TcpListener)ar.AsyncState;
// End the operation and display the received data on the console.
Console.WriteLine("Client connection completed");
Clients.Add(listener.EndAcceptTcpClient(ar));
// Signal the calling thread to continue.
TcpClientConnected.Set();
}
WebAPI Controller:
public class ValuesController : ApiController
{
// GET api/values/5
public string Get(int id)
{
return $"Foo: {id}";
}
}
As mentioned earlier, what I seek is "communication" between the WebAPI and the windows service. How can I pass the "id" parameter from the WebAPI call to the _engineService object in my windows service? Perhaps something similar to WPF's MVVM Light Messenger? The idea is that it would then be parsed and sent to the appropriate TcpClient that is stored in the Clients List.
Any advice on how to achieve this will be appreciated. Please feel free to ask for clarification/more code.
Did you find any answer to your issue yet ?
I don't quite understand what you try to achieve looking for a communication between the two of them ? Do you want to somehow rely on TCP/IP to relay this id or in-memory ?
Potentially, you could consider a Mediator pattern and use this kind of library that seems quite useful in the case I understood : https://github.com/jbogard/MediatR
In a simpler approach, I would rely on events to achieve what you are trying to do, which is having a reactive communication from the HTTP request to the c++ users.
Did I understand you needs ? I am quite curious about the solution
I'm assuming you are trying to take an HTTP GET request's ID parameter and send it to TCP clients who are connected to the EngineService. If your EngineService is initialized before your ApiService, I think this is a question of how to get a handle to the one-and-only EngineService instance from within an ApiService's controller instances.
If I'm following you, you could make the EngineService a public static property of your ContainerService and reference it as ContainerService.EngineService from the controller (or anywhere in the app for that matter) or better register your EngineService as a singleton in a DI container an inject it into the ApiService.
Solution (calls to WebAPI trigger EngineService)
I now use RabbitMQ/EasyNetQ to achieve communication between the WebApi and the EngineService object containing my TCP clients.
I have incidentally split them into two separate Projects/Topshelf services now.
The following is the new "communication" component and it is instantiated in the EngineService constructor.
public class Communication
{
private readonly Logger _logger;
private readonly IBus _bus;
public delegate void ReceivedEventHandler(string data);
public event ReceivedEventHandler Received;
protected virtual void OnReceive(string data)
{
Received?.Invoke(data);
}
public Communication()
{
_logger = new Logger();
_bus = RabbitHutch.CreateBus("host=localhost", reg => reg.Register<IEasyNetQLogger>(log => _logger));
SubscribeAllQueues();
}
private void SubscribeAllQueues()
{
_bus.Receive<Message>("pipeline", message =>
{
OnReceive(message.Body);
});
}
public void SubscribeQueue(string queueName)
{
_bus.Receive<Message>(queueName, message =>
{
OnReceive(message.Body);
});
}
}
An event handler is then added.
This means that as soon as a message arrives to the bus, the data will be relayed to the event handler which will subsequently relay it to the first connected TCP client in the list.
public void Handler(string data)
{
//Console.WriteLine(data);
_clients[0].Client.Send(Encoding.UTF8.GetBytes(data));
}
...
_comPipe.Received += Handler;
And finally on the WebApi's controller:
public string Get(int id)
{
ServiceCom.SendMessage("ID: " + id);
return "value";
}
ServiceCom class. Allows sending a string message on the bus.
public static class ServiceCom
{
public static void SendMessage(string messageBody)
{
var messageBus = RabbitHutch.CreateBus("host=localhost");
messageBus.Send("pipeline", new Message { Body = messageBody });
}
}
Now that this is done, I am now looking to implement a way for the connected TCP clients to trigger updates/events in an additional SPA project that will act as a Portal / Client Management App.
My approach will probably make use of KnockOut.js and SignalR to achieve dynamic Views where TCP client events are displayed immediately and similarly actions on to WebAPI will trigger events in the TCP clients. I know it sounds like a bizarre combination of processes but it is all according to plan and working out as expected :)

Async two-way communication with Windows Named Pipes (.Net)

I have a windows service and a GUI that need to communicate with each other. Either can send messages at any time.
I'm looking at using NamedPipes, but it seems that you cant read & write to the stream at the same time (or at least I cant find any examples that cover this case).
Is it possible to do this kind of two-way communication via a single NamedPipe?
Or do I need to open two pipes (one from GUI->service and one from service->GUI)?
Using WCF you can use duplex named pipes
// Create a contract that can be used as a callback
public interface IMyCallbackService
{
[OperationContract(IsOneWay = true)]
void NotifyClient();
}
// Define your service contract and specify the callback contract
[ServiceContract(CallbackContract = typeof(IMyCallbackService))]
public interface ISimpleService
{
[OperationContract]
string ProcessData();
}
Implement the Service
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
public class SimpleService : ISimpleService
{
public string ProcessData()
{
// Get a handle to the call back channel
var callback = OperationContext.Current.GetCallbackChannel<IMyCallbackService>();
callback.NotifyClient();
return DateTime.Now.ToString();
}
}
Host the Service
class Server
{
static void Main(string[] args)
{
// Create a service host with an named pipe endpoint
using (var host = new ServiceHost(typeof(SimpleService), new Uri("net.pipe://localhost")))
{
host.AddServiceEndpoint(typeof(ISimpleService), new NetNamedPipeBinding(), "SimpleService");
host.Open();
Console.WriteLine("Simple Service Running...");
Console.ReadLine();
host.Close();
}
}
}
Create the client application, in this example the Client class implements the call back contract.
class Client : IMyCallbackService
{
static void Main(string[] args)
{
new Client().Run();
}
public void Run()
{
// Consume the service
var factory = new DuplexChannelFactory<ISimpleService>(new InstanceContext(this), new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/SimpleService"));
var proxy = factory.CreateChannel();
Console.WriteLine(proxy.ProcessData());
}
public void NotifyClient()
{
Console.WriteLine("Notification from Server");
}
}
Using a single point to accumulate messages (a single pipe in this case) forces you to handle direction of the message yourself too (in addition to that you have to use a system-wide lock for the pipe).
So use 2 pipes with opposite directions.
(Another option would be using 2 MSMQ queues).
Your named pipe stream classes (server or client) have to be constructed with a PipeDirection of InOut. You need one NamedPipeServerStream, probably in your service, which can be shared by an arbitrary number of NamedPipeClientStream objects. Construct the NamedPipeServerStream with the name of the pipe and the direction, and the NamedPipeClientStream with the name of the pipe, the name of the server, and the PipeDirection, and you should be good to go.

WCF Timer based service not calling client back

I want to make a WCF timer service where clients can register in order to get called back from the service after a certain time has passed. The problem is that the client doesn't get called back. No Exception is thrown.
The callback interface is:
[ServiceContract]
public interface ITimerCallbackTarget
{
[OperationContract(IsOneWay = true)]
void OnTimeElapsed(int someInfo);
}
The service looks like:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Single)]
public class TimerService : ITimerService
private readonly Timer _timer = new Timer(2000); //System.Timers.Timer
public void Subscribe()
{
ITimerCallbackTarget listener =
OperationContext.Current.GetCallbackChannel<ITimerCallbackTarget>();
_timer.Elapsed += (p1, p2) =>
{
listener.OnTimeElapsed(999);
};
_timer.Start();
}
The callback method used by the client is:
private class TimerCallbackTarget : ITimerCallbackTarget
{
public void OnTimeElapsed(int someInfo)
{
Console.WriteLine(someInfo);
}
}
The client registers like this:
private static void TestTimerService()
{
InstanceContext callbackInstance = new InstanceContext(new TimerCallbackTarget());
using (DuplexChannelFactory<ITimerService> dcf =
new DuplexChannelFactory<ITimerService>(callbackInstance,
"TimerService_SecureTcpEndpoint"))
{
ITimerService timerProxy = dcf.CreateChannel();
timerProxy.Subscribe();
}
}
If I use a different thread at the subscribe method without Timer it works:
ThreadPool.QueueUserWorkItem(p =>
{
listener.OnTimeElapsed(999);
});
It even works with the Timer (for three seconds) if I put a Thread.Sleep(3000) at the end of the subscribe method so my guess is that maybe the channel to the callback-object gets closed after the subscribe method is finished. Using a class-scope variable for the callback object retrieved with OperationContext.Current.GetCallbackChannel(); instead of the method-scope variable didn't help.
Previously i tried creating new Threads in the elapsed event handler of the Timer of the timer service to make it faster. An ObjectDisposedException was thrown with the message: "Cannot access a disposed object. Object name: 'System.ServiceModel.Channels.ServiceChannel". I then tried to simplify my service and found that even using only the Timer causes problems as described but I guess the exception indicates that somewhere the connection to the client's callback object is lost. It's strange that there is no excepiton if I don't make new threads in the Timer thread. The callback method just isn't called.
In a duplex binding the lifetime of the two channels are linked. If the channel to the TimerService closes, then the callback channel to the CallbackTarget closes too. If you try to use a channel that was closed, you can get an ObjectDisposedExcpetion. In your case this is bad, because you don't want to keep the Subscribe() channel open just to receive OnTimeElasped() calls... and I'm assuming you want to subscribe for an infinitely long time.
A duplex channel is trying to make your life easier, but doesn't fit your needs. Behind the scenes a duplex channel is actually creating a second WCF service host for the CallbackTarget. If you create the client's service host manually to receive callbacks, then you can manage its lifetime independently of the Subscribe() channel.
Below is a fully functional command line program that demonstrates the idea:
Create a TimerService
Create a TimerClient to receive notificatioins
Pass the TimerClient's endpoint address to the TimerService as a part of the subscribe call
TimerService uses the address it got from Subscribe() to send notifications to the TimerClient.
Note that no channel is left open longer than needed to make a single call.
Standard disclaimer: This is intended to show how to create "duplex like" behavior. There's a lack of error handling and other short cuts.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Timers;
using System.ServiceModel.Description;
namespace WcfConsoleApplication
{
[ServiceContract]
public interface ITimerCallbackTarget
{
[OperationContract(IsOneWay = true)]
void OnTimeElapsed(int someInfo);
}
[ServiceContract]
public interface ITimerService
{
[OperationContract(IsOneWay = true)]
void Subscribe(string address);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Single)]
public class TimerService : ITimerService
{
private readonly Timer _timer = new Timer(2000);
private ChannelFactory<ITimerCallbackTarget> _channelFac;
private int _dataToSend = 99;
public void Subscribe(string address)
{
// note: You can also load a configured endpoint by name from app.config here,
// and still change the address at runtime in code.
_channelFac = new ChannelFactory<ITimerCallbackTarget>(new BasicHttpBinding(), address);
_timer.Elapsed += (p1, p2) =>
{
ITimerCallbackTarget callback = _channelFac.CreateChannel();
callback.OnTimeElapsed(_dataToSend++);
((ICommunicationObject)callback).Close();
// By not keeping the channel open any longer than needed to make a single call
// there's no risk of timeouts, disposed objects, etc.
// Caching the channel factory is not required, but gives a measurable performance gain.
};
_timer.Start();
}
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Single)]
public class TimerClient : ITimerCallbackTarget
{
public void OnTimeElapsed(int someInfo)
{
Console.WriteLine("Got Info: " + someInfo);
}
}
class Program
{
static void Main(string[] args)
{
ServiceHost hostTimerService = new ServiceHost(typeof(TimerService), new Uri("http://localhost:8080/TimerService"));
ServiceHost hostTimerClient = new ServiceHost(typeof(TimerClient), new Uri("http://localhost:8080/TimerClient"));
ChannelFactory<ITimerService> proxyFactory = null;
try
{
// start the services
hostTimerService.Open();
hostTimerClient.Open();
// subscribe to ITimerService
proxyFactory = new ChannelFactory<ITimerService>(new BasicHttpBinding(), "http://localhost:8080/TimerService");
ITimerService timerService = proxyFactory.CreateChannel();
timerService.Subscribe("http://localhost:8080/TimerClient");
((ICommunicationObject)timerService).Close();
// wait for call backs...
Console.WriteLine("Wait for Elapsed updates. Press enter to exit.");
Console.ReadLine();
}
finally
{
hostTimerService.Close();
hostTimerClient.Close();
proxyFactory.Close();
}
}
}
}

WCF InstanceContextMode.PerSession

if I use this: InstanceContextMode.PerSession, then for every client I initialize my service one time. Am I right?
If I call a method that have [OperationBehavior(TransactionScopeRequired = true)], JobImplement constructor gets called. Why?
My Service:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class JobImplement : IJob
{
public static int Sum = 0;
public JobImplement()
{
Sum++;
}
[OperationBehavior(TransactionScopeRequired = true)]
public void UselessMethod1()
{
}
[OperationBehavior(TransactionScopeRequired = true)]
public void AddEmployee(string Name, string Age)
{
}
public int GetSum()
{
return Sum;
}
}
EDIT 1:
Im using WSHttpBinding binding and here is my only client code.
My client:
static void Main(string[] args)
{
ServiceReference1.IJob Service1 = new ServiceReference1.JobClient();
Service1.UselessMethod1();//sum become [1]
Service1.AddEmployee("","");//sum become [2]
Console.WriteLine(Service1.GetSum());//show [2].
Console.WriteLine(Service1.GetSum());//show [2].
Console.WriteLine(Service1.GetSum());//show [2].
}
The service as written here uses the default value of the ServiceBehavior.ReleaseServiceInstanceOnTransactionComplete attribute, which is true.
This means that the methods enforcing transaction scope via TransactionScopeRequired will cause the service instance to be released and the next time a method is invoked, a new instance gets created despite your desired InstanceContextMode.
Changing the relevant attribute to false should solve the problem.
Looks like your new instance of the service created every time client calls your service. It might be because client terminated session (and your constructor). For instance client closed proxy, in this case next time client calls service, new session will be created. Did you check you are not closing proxy every time you connect to the service?

WCF swap service interfaces

I'm new with WCF and still experimenting.
I have two ServiceContract like this:
[ServiceContract]
public interface IFirst
{
[OperationContract(IsOneWay = true)]
void First();
}
[ServiceContract]
public interface ISecond
{
[OperationContract(IsOneWay = true)]
void Second();
}
On server side Client class implementing those interfaces:
public class Client : IFirst, ISecond
{
static int count = 0;
int id;
public Client()
{
count++;
id = count;
Console.WriteLine("{0} client created.", id);
}
public void First()
{
Console.WriteLine("First from: " + id.ToString());
}
public void Second()
{
Console.WriteLine("Second: " + id.ToString());
}
}
and host:
ServiceHost host = new ServiceHost(typeof(Client), new Uri("net.tcp://localhost:8000"));
NetTcpBinding binding = new NetTcpBinding();
host.AddServiceEndpoint(typeof(IFirst), binding, "");
host.AddServiceEndpoint(typeof(ISecond), binding, "");
host.Open();
On client side:
ChannelFactory<IFirst> firstFactory = new ChannelFactory<IFirst>(new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:8000"));
IFirst iFirst = firstFactory.CreateChannel();
iFirst.First();
ChannelFactory<ISecond> secondFactory = new ChannelFactory<ISecond>(new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:8000"));
ISecond iSecond = secondFactory.CreateChannel();
iSecond.First();
This works fine. It calls First and then Second method but for each call creates new instance of Client class. It is obvious because when client creates channel, service creates new instance of Client object. What I wish to achieve is call Second after First but for the same instance of Client. Is it even possible? I know I can put this two methods in one service but it is not what I wish for.
As per my understanding you want to call the two methods while creating only one client.
In WCF you can control Instancing by setting InstanceContextMode property of Service Behavior Attribute. There are three possible values
-PerCall
-PerSession
-Single
You can use PerSession as it keeps the object active for the next calls from client.
The object is released when the session ends
You can decorate your class
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
Hope this helps.
At first I was a little confused by you calling your Service Implementation "Client" :P
But anyway, by default, WCF uses InstanceContextMode.PerCall, which means that it will instantiate a new Service implementation instance for every request into the Service.
If you want for subsequent service calls to be handled within the same service instance, you will have to use either PerSession or Single InstanceContextModes.
http://msdn.microsoft.com/en-us/library/ms731193.aspx summarises Sessions, Instances and Concurrency in WCF pretty well.

Categories

Resources