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.
Related
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.
So I started to create TDD tests for a new WCF service I wanted to create. In TDD, you create the test, and start adding objects that don't exist yet into your tests. First object I wanted to test was that the service is not null after instantiated.
Then then I said wait, how the hell can you do that in WCF...a service extension is .svc. Yes there's a .svc.cs but the main parent is .svc. I don't see a way I can to TDD on this part where you've have something like this but it'd create an .svc instead of just a .cs:
[TestMethod]
public void UponInitialization_ServiceIsNotNull()
{
// Arrange
// Act
NyNewService service = new MyNewService();
// Assert
Assert.IsTrue(service != null);
}
This is a very fundamental necessary first test, testing the very simplest possible from the start...this is very common on any TDD or XP team doing TDD.
Any thoughts on how I can create a new .svc from within my test here? not a plain .cs?
I'd start with creating acceptance test for non-existing WCF service:
private Uri _baseAddress = new Uri("http://localhost:8713/service1");
private IService1 _client;
[SetUp]
public void Setup()
{
var binding = new BasicHttpBinding();
var endpoint = new EndpointAddress(_baseAddress);
var factory = new ChannelFactory<IService1>(binding, endpoint);
_client = factory.CreateChannel();
}
[TearDown]
public void TearDown()
{
if (_client != null)
((ICommunicationObject)_client).Close();
}
[Test]
public void ShouldReturnSampleData()
{
Assert.That(_client.GetData(42), Is.EqualTo("You entered: 42"));
}
Keep in mind, that nothing is created yet - we start from test. Now you can create service interface:
public interface IService1
{
string GetData(int value);
}
Test now compiles, but of course, it fails with error
Attempted to get contract type for IService1, but that type is not a
ServiceContract, nor does it inherit a ServiceContract.
Good, that's because we should mark our interface with [ServiceContract] attribute. We add this attribute and run test again:
Method GetData is not supported on this proxy, this can happen if the
method is not marked with OperationContractAttribute or if the
interface type is not marked with ServiceContractAttribute.
Ok, mark our service interface with required attributes:
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(int value);
}
Now we see another error (because nothing is actually running our non-existing service)
There was no endpoint listening at http://localhost:8713/service1 that
could accept the message. This is often caused by an incorrect address
or SOAP action. See InnerException, if present, for more details.
We can use ServiceHost to run our service (at this moment we need to create service class to compile tests):
private ServiceHost _host;
[SetUp]
public void Setup()
{
_host = new ServiceHost(typeof(Service1), _baseAddress);
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
_host.Description.Behaviors.Add(smb);
_host.Open();
// creating client as above
}
[TearDown]
public void TearDown()
{
// closing client as above
if (_host != null)
_host.Close();
}
You also need to implement IService1 interface by service class (otherwise test will fail):
public class Service1 : IService1
{
public string GetData(int value)
{
throw new NotImplementedException();
}
}
Now I'd created some unit-tests for Service1 class in order to implement GetData functionality. With these tests passing you will have acceptance test passing also. Thats it. You did test first, and your WCF service is completely ready to be hosted.
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.
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 a ServiceHost listening on a NetNamedPipeBinding endpoint. I have a service contract class with a single method which is being called by the client and handled by the server. The method (We'll call it PipeRequest()) has a Request parameter. On the client side I populate this object but it's empty by the time it gets sent over to the server. Any ideas why this would be the case?
_Host = new ServiceHost(typeof(PipeService), new Uri(ServiceRequestRouter.URI));
_Host.AddServiceEndpoint(
typeof(IPipeService),
new NetNamedPipeBinding(),
_PipeName
);
_Host.Open();
[ServiceContract(Namespace = "http://www.example.com/PipeCommunication")]
interface IPipeService
{
[OperationContract]
void PipeRequest(ServiceRequestBase request);
}
[DataContract]
[KnownType(typeof(DerivedServiceRequest))]
[KnownType(typeof(SomeEnumType))]
public abstract class ServiceRequestBase
{
...
public void Dispatch(string pPipeName = ServiceRequestRouter.DefaultPipeName)
{
EndpointAddress epa = new EndpointAddress(_address_));
IPipeService proxy = ChannelFactory<IPipeService>.CreateChannel(new NetNamedPipeBinding(), epa);
proxy.PipeRequest(this);
}
}
It look like it has to do with proxy.PipeRequest(this);
You need to pass in a class that inherits ServiceRequestBase, if you class does inherit the ServiceRequestBase then it might not be serializable.
It turns out I had to specify (as part of the data contract) any derived classes from ServiceRequestBase class.
[DataContract]
[KnownType(typeof(CitrixInfoServiceRequest))] // added this line
[KnownType(typeof(RegStateServiceRequest))] // added this line
public abstract class ServiceRequestBase
{
// ...
}