I have a WCF service hosted on a console application the code is:
public interface ITestService
{
[OperationContract]
void SetField(string data);
[OperationContract]
string GetField();
}
public class TestService : ITestService
{
private string myData;
public string GetField()
{
retrun myData;
}
public void SetField(string data)
{
myData = data;
}
}
then I hosted it on a console application:
ServiceHost host = new ServiceHost(typeof(TestService));
host.Open();
Console.WriteLine("Test Service Host");
Console.WriteLine("Service Started!");
foreach (Uri address in host.BaseAddresses)
{
Console.WriteLine("Listening on " + address);
}
Console.WriteLine("Press any key to close the host...");
Console.ReadLine();
host.Close();
I started the console host then In an other console app I referenced the service and used it:
TestService client = new TestService();
client.SetField("test");
Console.WriteLine( client.GetField() );
this print nothing means the field is still null
What is wrong with this service?
What's wrong is that you're expecting that state will be persisted between calls - it is NOT. By default, WCF are absolutely stateless (and they should be! That's a good thing!)
If you need to persist information - store it into a persistent store (a.k.a a database).
Each WCF call will (by default) get a brand new, freshly created instance of TestService.
So your second call's instance knows nothing about the first instance (used by SetField) and therefore cannot return that value that you set in the first call.
Try this:
Use string as static.
public interface ITestService
{
[OperationContract]
void SetField(string data);
[OperationContract]
string GetField();
}
public class TestService : ITestService
{
private static string myData;
public string GetField()
{
retrun myData;
}
public void SetField(string data)
{
myData = data;
}
}
You should mark your service class with the attribute:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class TestService : ITestService
{
//...
}
This means that your service must have only one instance. And you must create the host like this:
var host = new ServiceHost(new TestService()); // or get a singleton..
host.Open();
Pay your attantion that you use an instance to create a service instead type. Then your code should work.
Related
I have a Windows service that uses net.pipe as a WCF server and a Windows forms client to connect to it. There is a class called ConfigSettings that has values I want the client to query.
I want to have the client read the current values inside the serviceConfig instance that the service uses. Ultimately, I want the client to change values in it, but baby steps first.
The form can talk to the server via named pipes, but 'return serviceConfig is sending a new empty instance back to the client. I want the data that the service is actively using (that is, serviceConfig.Setting1 = x; serviceConfig.Setting2 = "foo"; )
The Windows service and WCF server code is (updated to working version):
using System.IO;
using System.ServiceModel;
using System.ServiceProcess;
namespace WindowsServiceTest
{
public partial class Service1 : ServiceBase
{
internal static ServiceHost myServiceHost = null;
//this is the master config that the service uses
public static ConfigSettings serviceConfig = new ConfigSettings();
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
if (myServiceHost != null)
{
myServiceHost.Close();
}
myServiceHost = new ServiceHost(typeof(WCFService1));
myServiceHost.Open();
//set active default settings
serviceConfig.Setting1 = 1;
serviceConfig.Setting2 = "initial.Setting2:" + serviceConfig.Setting1;
}
protected override void OnStop()
{
if (myServiceHost != null)
{
myServiceHost.Close();
myServiceHost = null;
}
}
}
public partial class WCFService1 : IService1
{
public ConfigSettings GetConfig()
{
return Service1.serviceConfig;
}
public void SetConfig(ConfigSettings sentConfig)
{
Service1.serviceConfig = sentConfig;
}
}
[ServiceContract]
public interface IService1
{
[OperationContract]
ConfigSettings GetConfig();
[OperationContract]
void SetConfig(ConfigSettings sentConfig);
}
public class ConfigSettings
{
public int Setting1 { get; set; }
public string Setting2 { get; set; }
public ConfigSettings() { }
}
}
The client retrieves the config like this (updated with some changes):
using System;
using System.ServiceProcess;
using System.Windows.Forms;
using WindowsServiceTest;
namespace WindowsServiceTestForm
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
ConfigSettings config = new ConfigSettings();
//GetConfig()
private void button1_Click(object sender, EventArgs e)
{
ServiceReference1.Service1Client myService = new ServiceReference1.Service1Client();
ServiceControllerPermission scp = new ServiceControllerPermission(ServiceControllerPermissionAccess.Control, Environment.MachineName, "Service1");//this will grant permission to access the Service
//get the current config and display
config = myService.GetConfig();
MessageBox.Show(config.Setting1 + "\r\n" + config.Setting2, "config");
myService.Close();
}
//SetConfig(ConfigSettings)
private void button2_Click(object sender, EventArgs e)
{ //make changes to values
config.Setting1 += 1;
config.Setting2 = "new.Setting2:" + config.Setting1;
ServiceReference1.Service1Client myService = new ServiceReference1.Service1Client();
ServiceControllerPermission scp = new ServiceControllerPermission(ServiceControllerPermissionAccess.Control, Environment.MachineName, "Service1");//this will grant permission to access the Service
//send the new config
myService.SetConfig(config);
myService.Close();
}
}
}
Update:
Maybe what I'm thinking needs to be done is overkill. It seems that I'm hitting a membrane between WCF and the Windows Service.
How would YOU approach this problem?
Windows Service that needs a Form for configuration.
When service starts, it loads a config.xml file from disk. (a serialized class)
When GUI starts, I want to:
retrieve its current configuration,
make some changes to it,
push it back to the service,
trigger service to re-read and react to the new configuration.
I was trying to avoid statically/writing the config file to disk and telling service to re-read it again. It "seemed" like WCF was the way to go.
Update 2
It seems that by just changing the master config in the service to static, the WCF service can access it directly. I could have sworn I did that originally before I posted, but I guess not.
I also separated the naming of Service1 to WCFService1 above, but it turns out that doesn't matter and works either way.
New complete code has been updated above.
You are getting confused between Windows Service and WCF Service - and have tried to have them both in the same class - while this is possible - it is probably easier to understand if you split them into two classes.
In your example the Windows Service starts, creates a new instance of itself as the WCF service then sets the config elements in the Windows Service instance, meaning the config is empty in the WCF Service instance.
try this instead
using System.ServiceModel;
using System.ServiceProcess;
namespace WindowsServiceTest
{
public partial class Service1 : ServiceBase
{
internal static ServiceHost myServiceHost = null;
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
if (myServiceHost != null)
{
myServiceHost.Close();
}
myServiceHost = new ServiceHost(typeof(WCFService1 ));
myServiceHost.Open();
}
protected override void OnStop()
{
if (myServiceHost != null)
{
myServiceHost.Close();
myServiceHost = null;
}
}
}
public class WCFService1 : IService1
{
public WCFService1()
{
//change master settings from null
myConfig.Setting1 = "123";
myConfig.Setting2 = "456";
}
//this is the master config that the service uses
public ConfigSettings myConfig = new ConfigSettings();
public ConfigSettings GetConfig()
{
return myConfig;
}
}
[ServiceContract]
public interface IService1
{
[OperationContract]
ConfigSettings GetConfig();
}
public class ConfigSettings
{
public string Setting1 { get; set; }
public string Setting2 { get; set; }
public ConfigSettings()
{ }
}
}
There are a few ways of doing this (injecting dependencies in the service instance). I'll show two of them.
You have a special case here because the Windows Service is also your WCF service implementation. I think that soon enough you will want to separate them. So, my samples are based on a separate WCF service implementation.
The thing is that WCF has a concept of service instance mode. By default it is PerCall, meaning that a new Service1 instance is created to handle each request. This makes it a bit more difficult to inject something in these instances.
The simplest way is to set the instance mode to Single, meaning there will be only one Service1 instance handling all requests.
The second is easy to implement:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Service1Implementation : IService1
{
private ConfigSettings _configSettings;
public Service1(ConfigSettings settings)
{
//now you have the settings in the service
_configSettings = setting;
}
...
}
//in the Windows Service
myServiceHost = new ServiceHost(typeof(Service1Implementation), new Service1Implementation(myConfig));
myServiceHost.Open();
The first solution involves you creating and specifying a service instance factory which the infrastructure will use later to create service instances per call.
The factory gives you the possibility to instantiate Service1 yourself and pass the config.
There is quite some code to be written for this, but I'll show the essential. For the complete solution, read this.
For you, it's easier to make the Windows Service just implement IInstanceProvider:
public class Service1 : ServiceBase, IInstanceProvider
{
private ConfigSettings _myConfig; //assign this member later on
...
//IInstanceProvider implementation
public object GetInstance(InstanceContext instanceContext, Message message)
{
//this is how you inject the config
return new Service1Implementation(_myConfig);
}
public object GetInstance(InstanceContext instanceContext)
{
return this.GetInstance(instanceContext, null);
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
}
...
}
I will admit that it's been a while since I've touched WCF, but it looks to me like your ConfigSettings class is missing some attributes required to make it serializable via WCF.
using System.Runtime.Serialization;
[DataContract]
public class ConfigSettings
{
[DataMember]
public string Setting1 { get; set; }
[DataMember]
public string Setting2 { get; set; }
public ConfigSettings()
{ }
}
I don't believe having your Windows service operate as your WCF service, like other answers have suggested, is the problem. But I do agree that it's best to have them be separate classes.
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.
This is my code for server application:
[ServiceContract]
public interface IFirst
{
[OperationContract]
void First();
}
[ServiceContract]
public interface ISecond
{
[OperationContract]
void Second();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
class Service : IFirst, ISecond
{
static int count = 0;
int serviceID;
public Service()
{
serviceID = ++count;
Console.WriteLine("Service {0} created.", serviceID);
}
public void First()
{
Console.WriteLine("First function. ServiceID: {0}", serviceID);
}
public void Second()
{
Console.WriteLine("Second function. ServiceID: {0}", serviceID);
}
}
class Server
{
static void Main(string[] args)
{
ServiceHost host = new ServiceHost(typeof(Service), new Uri("net.tcp://localhost:8000"));
NetTcpBinding binding = new NetTcpBinding();
host.AddServiceEndpoint(typeof(IFirst), binding, "");
host.AddServiceEndpoint(typeof(ISecond), binding, "");
host.Open();
Console.WriteLine("Successfully opened port 8000.");
Console.ReadLine();
host.Close();
}
}
and client:
class Client
{
static void Main(string[] args)
{
ChannelFactory<IFirst> firstFactory = new ChannelFactory<IFirst>(new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:8000"));
IFirst iForst = firstFactory.CreateChannel();
iForst.First();
ChannelFactory<ISecond> secondFactory = new ChannelFactory<ISecond>(new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:8000"));
ISecond iSecond = secondFactory.CreateChannel();
iSecond.Second();
Console.ReadLine();
}
}
When I run it I get output:
Successfully opened port 8000.
Service 1 created.
First function. ServiceID: 1
Service 2 created.
Second function. ServiceID: 2
In my case server creates two instances of Service. What I want to do is call Second function for the same Service instance that First did.
You can do two things:
Move Second to IFirst so
public interface IFirst
{
[OperationContract]
void First();
[OperationContract]
void Second();
}
Or use a Singleton for the service instance
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
class Service : IFirst, ISecond
{
...
}
Change your behaviour to single
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
As you are using InstanceContextMode.PerSession that means service creates a session for each client as You are trying to connect to service from two clients thats why it is creating two instances of Service
by changing it to InstanceContextMode.Single only one instance of Service will serve both of your clients.
From MSDN
The System.ServiceModel.InstanceContext manages the association
between the channel and the user-defined service objects. Use the
InstanceContextMode enumeration with the
ServiceBehaviorAttribute.InstanceContextMode property to specify the
lifetime of the InstanceContext object. can create a new
InstanceContext object for every call, every session or specify that
the InstanceContext object is bound to a single service object.
The Single value specifies that a single InstanceContext object should be used for the lifetime of the service.
I know the post is old, but for others.
What you can do is combine your IFirst and ISecond into ICombinedService.
Then you would create a single channel in your client - meaning a single instance of your service host session would be created.
In your current code you are creating a connection for IFirst, and ISecond [These are the two session instances you created.]
IFirst iForst = firstFactory.CreateChannel(); // First Session Created!
ISecond iSecond = secondFactory.CreateChannel(); // Second Session Created!
To change that behavior you will need to combine the services into one service; and can make per session calls on that.
class Client
{
static void Main(string[] args)
{
ChannelFactory<ICombinedFirstSecond> combinedFactory = new ChannelFactory<ICombinedFirstSecond>(new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:8000"));
ICombinedFirstSecond iCombinedFirstSecond = combinedFactory.CreateChannel();
iCombinedFirstSecond.First();
iCombinedFirstSecond.Second();
Console.ReadLine();
}
}
This is what I think you really are looking for and not a singleton service.
I'm tring to implement my first WCF call-back server. This is my code:
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(ILogCallback))]
public interface ILog
{
}
public interface ILogCallback
{
[OperationContract(IsOneWay = true)]
void Push(string callbackValue);
}
public class MyLog : ILog
{
}
class Log
{
public static void initialize()
{
using (ServiceHost host = new ServiceHost(
typeof (MyLog),
new Uri[]
{
new Uri("net.pipe://localhost")
}))
{
host.AddServiceEndpoint(typeof (ILog),
new NetNamedPipeBinding(),
"PipeReverse");
host.Open();
// TODO: host.Close();
}
}
public static void Push(string s)
{
ILogCallback callbacks = OperationContext.Current.GetCallbackChannel<ILogCallback>();
callbacks.Push(s);
}
}
then I try to use my server using this code:
Log.initialize();
while (true)
{
Log.Push("Hello");
System.Threading.Thread.Sleep(1000);
}
But I got NPE, because OperationContext.Current is null. Why, what's wrong and how to fix that?
Because you are NOT in the context of an operation.
You're simply calling a static method of the Log class.
For you to be in an Operation Context your call MUST have been come from a client that is being serviced by your WCF server.
OperationContext.Current is a thread-static property that is initialized when request arrives to the server. Here's what you do to call the callback
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(ILogCallback))]
public interface ILog
{
void PushOnTheClient();
}
public class MyLog : ILog
{
void PushOnTheClient()
{
ILogCallback callbacks = OperationContext.Current.GetCallbackChannel<ILogCallback>();
callbacks.Push(s);
}
}
You are missing the subscriber's subscription. The way you do this is to create a [oneway] operation in your MyLog WCF server called something like: "void SendMeLogs()".
This will open the client callback channel. You Then have to implement SendMeLogs() in lines of something like:
void SendMeLogs()
{
while(CheckLogsForNewData())
{
PushOnTheClient();
}
}
Since the SendMeLogs() function is oneway, the client will not block, but will start the subscription to your log server. (you can search the net for sample code for duplex calculator in wcf for a good example of this architecture).
The key however is that you must have a nice unsubscribe method like "StopSendingMeLogs" to break the loop, and also make the PushOnTheClient function fail safe, in case the client terminates or the specific client connection goes down.
The "CheckLogsForNewData" function should ideally be a shared (static) implementation in your case
I have added remoting calls to my Windows Service so my GUI application can talk to it. It works great, but my channel implementation has no knowledge of my service.
How should I layer my classes so that my remoting channel implementation can call methods in my service class?
Channel Interface:
public interface IMyService
{
string Ping();
string SomeMethod(string input);
}
Channel Implementation:
public class MyServiceChannel : MarshalByRefObject, IMyService
{
public string Ping()
{
return "Pong";
}
public string SomeMethod(string input)
{
MethodForChannelToCall(input); // in Service class. How to reference?
return "Some Output";
}
}
Service Class
class MyService : ServiceBase
{
public void MethodForChannelToCall(string input)
{
// do service stuff for remoting call
}
public MyService()
{
// Set up remoting channel
try
{
TcpChannel tcpChannel = new TcpChannel(12345);
ChannelServices.RegisterChannel(tcpChannel, false);
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(MyServiceChannel),
"MyServiceChannel",
WellKnownObjectMode.SingleCall);
// Should I pass an instance of my service to my channel somehow here?
}
catch (Exception ex)
{
this.EventLog.WriteEntry("Remoting error: " + ex.ToString());
}
}
}
How should I structure my classes so that my channel can call my service methods?
In this case you should use WCF. WCF replaces .net remoting.
If both your client and service are on the same machine you can use named pipes binding.
If they are on different machines you can use net tcpip binding.