Calling one WCF service from another... or am I? - c#

I'm trying to make a call to a remote WCF service from within an existing service.
I've added a Service Reference to the method I need to consume in the remote service, and use it as follows in this WebMethod of my own service:
[WebMethod(Description = "My local service."]
public RemoteService.ServiceResponse ServiceRequest(RemoteService.SendRequest myObject)
{
// Instance of remote service's method I'm
RemoteService.ServiceResponse SendResponse;
SendResponse = ServiceRequest(RemoteService.SendRequest)
return SendResponse;
}
My question, with the call to the ServiceRequest web method of the remote service, am I actually calling the remote service?! Or, am I just calling my own local instance of the remote service's ServiceRequest method?
If I'm right about my being wrong, what would be the proper way to do what I need to do, to kind of act I guess as a passthrough or proxy to pass requests and responses to and from my service and the remote service?

First of all, the [WebMethod] attribute would point to ASMX webservice - not WCF. Is it really WCF??
Second, if it IS WCF: in order to call a method on a service, you need to instantiate a proxy client for that service. When you generated your service reference, you should have gotten a ServiceNamespace.ServiceReferenceClient class of sorts - it's been autogenerated for you. You need to instantiate this and call the method on that proxy:
[WebMethod(Description = "My local service."]
public RemoteService.ServiceResponse ServiceRequest(RemoteService.SendRequest myObject)
{
// Instance of remote service's method I'm
RemoteService.ServiceResponse SendResponse;
ServiceProxyClient client = new ServiceProxyClient();
SendResponse = client.ServiceRequest(RemoteService.SendRequest)
return SendResponse;
}
That way, you are indeed calling the service you just added as a Service Reference.

Related

Calling service from self host

I have a very complex scenario in which my services are dynamically loaded and everything is done programmatically. In particular I have a service that has 2 endpoints
net.pip://localhost/test
net.pipe://localhost/test/mex
I have a client that access this without issue as well as the WCF Test Client tool.
I am trying to access the service from the selfhost wrapper. I have read you just treat it as a client and create a factory and channel but something is preventing this from working. The same code that works on the client will not work in the wrapper. The code just
private IAgentBase GetLocalClient(string serviceEndpointName)
{
var factory = new ChannelFactory<IAgentBase>(serviceEndpointName);
return factory.CreateChannel();
}
This does return a proxy and then I call a method on the service...
var proxy = GetLocalClient("net.pipe://localhost/test");
proxy.DoThis();
But the code just goes someplace - keeps running and no error. The statement never completes. When I step over that line or set a BP or a try catch, it never completes the method call
Change your GetLocalClient to the following
public IAgentBase GetlocalClient(string ed)
{
EndpointAddress edi = new EndpointAddress(ed);
var channel = ChannelFactory<IAgentBase>.CreateChannel(new NetNamedPipeBinding(), edi);
return channel;
}

Should I be using wsHttpBinding or wsDualHttpBinding for methods that return values?

I am trying to write a host/client style system with WCF and I currently want to use the wsHttpBinding but I was reading up about something else (the isOneWay bool) when I cam across this line:
'HTTP can't be used for callbacks and therefore you can't use callbacks over BasicHttpBinding or WSHttpBinding'
Most of my methods will return values, for example the client would call a method which would send back specific data from the server depending on the method called. Like this, with a return line in:
public string SayHello(string name)
{
Console.WriteLine(">>> SayHello has been called");
return string.Format("Hello, {0}!", name);
}
Would the return line make it a callback so would I have to use a different binding? Or is a callback only when the client sends a 'message' to the server and the server sends a 'message' back? In my code it is just calling the method which returns data so would that not be classed as a callback and I can continue to use the wsHttpBinding? Thanks
You definitely don't need wsDualHttpBinding to return values. BasicHttpBinding or even better WSHttpBinding is what you need
wsDualHttpBinding is used to actually call the client from the server (callbacks). It's something which isn't widely used in the web world because it has limitations and there are better options like WebSockets and SignalR

wcf interface: why doesn't it 'just' go to the methode but to the whole class

I have WCF service implemented and the connection works just fine. I use BasicHttpBinding.
[ServiceContract]
public interface IScannerInput
{
[OperationContract]
string ScannedPRX(string barcode, string user, int color);
}
public class ProcessPRX : IScannerInput
{
ProcessClass c = new ProcessClass(); // every time a call ScannedPRX() this class is made again
public string ScannedPRX(string barcode, string user, int color)
{
c.PrxScannedInput(barcode, user, color);
return "Bussy processing: " + barcode;
}
}
In a normal class I can just make ProcessClass c one time. But now it is made again and again every time a call the methode ScannedPRX(). What am I doing wrong? It is not just going to the methode but to the whole class.
There is three ways of instantiating WCF service object:
PerCall: A new InstanceContext (and therefore service object) is created for each client request.
PerSession: A new InstanceContext (and therefore service object) is created for each new client session and maintained for the lifetime of that session (this requires a binding that supports sessions).
Single: A single InstanceContext (and therefore service object) handles all client requests for the lifetime of the application.
PerCall is default one, and that is what you are having.
If you want other behaviour read article below.
http://msdn.microsoft.com/en-us/library/ms731193.aspx
Take into account concurrent request that are made to your service, as if you are choosing for instance Single instantiation mode, you need to take care on your own that all your methods are thread safe.
Because the default instancing behavior for WCF services is to create a new instance for every call. You generally want this to avoid sharing state between different callers of your service or multiple invocations by the same client. Unless ProcessClass is expensive to create or you need to maintain state between calls, I would stick with this model.

How to enable user GUI response in wcf service to trigger duplex callback to client

Maybe I am trying the impossible...
I have created a wpf application to start a wcf service with the following service contract:
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IRejectCallback))]
public interface IRejectService
{
[OperationContract(IsOneWay = true)]
void SubmitNewRejectInfo();
[OperationContract(IsOneWay = true)]
void SendRejectCallback();
}
My service behavior:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Reentrant, UseSynchronizationContext = true)] //(ConcurrencyMode = ConcurrencyMode.Single, InstanceContextMode = InstanceContextMode.Single, UseSynchronizationContext = true)]
public class RejectService : IRejectService, IPostRejectEvent
I create my duplex channel and call the SubmitNewRejectInfo service method:
InstanceContext ic = new InstanceContext(new RejectCallbackHandler());
tcpFactory = new DuplexChannelFactory<IRejectService>(ic, "netTcp");
IRejectService _rejectService = tcpFactory.CreateChannel();
_rejectService = _tcpFactory.CreateChannel();
_rejectService.SubmitNewRejectInfo();
The SubmitNewRejectInfo method is run on the service side. Normally, I would add my callback method like this:
public void SubmitNewRejectInfo(RejectInformation rejectInformation)
{
// Do something here...
callback.RejectCallback();
}
However, when the SubmitNewRejectInfo method is run from the client (using IsOneWay = true), I do not want to callback to the client at that time. I would like to wait for the user to click a button on my WPF GUI which will transmit the signal to callback to the client. ** Is it possible to postpone the callback, or send a callback via a different operation contract? **
How could the client invoke the service via an operation contract method and then receive a callback after user interaction happens on the service side? I saw one duplex example where someone used a reentrant service with Thread.Sleep() as follows:
public void Register()
{
OperationContext ctxt = OperationContext.Current;
IClientCallback callBack = ctxt.GetCallbackChannel<IClientCallback>();
Thread.Sleep(3000);
callBack.TempUpdate(10);
}
In my case, I would need to trigger the callback in the Register method after a user clicks a button on a gui that is hosting the service. Would this be possible? Any ideas?
UPDATE **
I have discovered my main issue:
I make a call from my wcf client to my operation service contract method:
InstanceContext ic = new InstanceContext(new RejectCallbackHandler());
_tcpFactory = new DuplexChannelFactory<IRejectService>(ic, "netTcp");
_rejectService = _tcpFactory.CreateChannel();
_rejectService.SubmitNewRejectInfo();
The wcf service operation is invoked here:
public void SubmitNewRejectInfo(RejectInformation rejectInformation)
{
// Throw event to notify MainViewModel that new reject information is available.
OnSubmitNewRejectInfo(new RejectInfoArgs(rejectInformation));
callback.RejectCallback();
}
The event is fired to notify my MainViewModel that some data has been updated and refresh some properties. Then problem begins... I do not want the callback.RejectCallback(); to fire just yet. I need the user to click a button on my Mainwindow GUI associated to the view model to "authorize" the duplex callback to return a message to the wcf client.
Any ideas how to "pause" the callback long enough for a user to click a button to authorize the duplex callback to deliver a message to the wcf client? Perhaps my OnSubmitNewRejectInfo event can return some event argument before the callback is invoked? Could a new delegate be triggered to return information from my MainViewModel before the callback is invoked?
I hope this describes my problem a little better. Any help is VERY much appreciated.
Update number 2 **
More information... :)
The WCF service was created as a WCF service class library. The WCF client was also created as a WCF service class library. This makes it easy for other applications or class objects to host the service and client. This was done in order for human interaction via a GUI on the service side, and other software interaction on the wcf client side. The WCF service and client are hosted on separate machines.
The WCF service is hosted by a WPF application, and communication is event driven between the two. The service class is created as a singleton in the MainViewModel of the WPF application.
The WCF service class must talk via duplex communication with the wcf client. The client invokes an operation contract to update information in the service, which is displayed on the WPF GUI. After the information is displayed on the GUI, then the user must click a button to invoke the callback to the client indicating that the service has completed it's task.
So, WPF app hosts a wpf service class library. There is communication between the WPF app and service class via events. The service class is consumed by a wcf client via duplex channel communication. The wcf client is also hosted by another class object with a service reference to the wcf service. The client communicates with it's host via events.
WCF CLIENT CODE:
InstanceContext ic = new InstanceContext(new RejectCallbackHandler());
_tcpFactory = new DuplexChannelFactory<IRejectService>(ic, "netTcp");
_rejectService = _tcpFactory.CreateChannel();
_rejectService.SubmitNewRejectInfo(); // This is where I invoke a service operation from my client.
WCF SERVICE CODE:
// This service operation is consumed by the client.
public void SubmitNewRejectInfo(RejectInformation rejectInformation)
{
// Create event to notify MainViewModel that new reject info is available.
OnSubmitNewRejectInfo(new RejectInfoArgs(rejectInformation));
// **** I need something to happen here in order to halt the duplex callback to the client until a human creates a button click event in my MainViewModel, which indicates the duplex callback may be sent back to the client. ****
callback.RejectCallback();
}
Sorry this question has become very detailed. I never should have fallen asleep during my technical writing class in college... :)
Update number 3 **
I tried running the code that degorolls mentioned below. His example code is perfect for my needs!! (Thanks degorolls!) However I get a null reference exception: "Object reference not set to an instance of an object".
First the action executes in this part of degoroll's demo code:
if (pendingNotifications.TryGetValue(rejectInformation, out action))
{
try
{
action(rejectInformation); // This is invoked
Then this part of the demo code is called -> callback.RejectCallback(new RejectCallbackMessage())); :
public void SubmitNewRejectInfo(RejectInformation rejectInformation)
{
// Throw event to notify MainViewModel that new reject information is available.
OnSubmitNewRejectInfo(new RejectInfoArgs(rejectInformation));
pendingNotifications.Add(rejectInformation, info => callback.RejectCallback(new RejectCallbackMessage())); // **** the action returns to callback.RejectCallback here ****
This is where I get my null exception error.
Here is my code to get the callback channel:
IRejectCallback callback
{
get { return OperationContext.Current.GetCallbackChannel<IRejectCallback>(); }
}
My guess is that I am not returning null instead of the original callback channel...
Is there a way I can obtain the correct channel at this point in the code?
If I'm understanding things correctly it seems that the server simply needs to be keep a list of things it is waiting to do. How you implement will be tied closely to the instancing of the server. If you stick with singleton, you can simply hold a map of pending notification in the server class. E.g.:
public class RejectService
{
Dictionary<RejectInformation, Action<RejectInformation>> pendingNotifications = new Dictionary<RejectInformation, Action<RejectInformation>>();
public void SubmitNewRejectInfo(RejectInformation rejectInformation)
{
OnSubmitNewRejectInfo(new RejectInfoArgs(rejectInformation));
pendingNotifications.Add(rejectInformation, info => callback.RejectCallback(info));
}
public void SendRejectCallback(RejectInformation rejectInformation)
{
Action<RejectInformation> action;
if (pendingNotifications.TryGetValue(rejectInformation, out action))
{
acion(rejectInformation);
pendingNotifications.Remove(rejectInformation);
}
}
}
If you want to make this reentrant you may need to think about locks... This is a really simplistic approach but gives a starting point.

How do I get a reference to a WCF service object from the code that creates it?

I'm modifying the code in this tutorial to build some basic subscribe push wcf client/server classes, and I've just hit a bit of a brick wall.
The server class in the tutorial is created using the following code:
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(
typeof(StringReverser),
new Uri[]{
new Uri("net.pipe://localhost")
}))
{
host.AddServiceEndpoint(typeof(IStringReverser),
new NetNamedPipeBinding(),
"PipeReverse");
host.Open();
Console.WriteLine("Service is available. " +
"Press <ENTER> to exit.");
Console.ReadLine();
host.Close();
}
}
}
Which I assume publishes an instance of StringReverser my problem is I need a reference to that instance so I can call a method on it to push data back to the client.
In the tutorial the server just replies to the client using a callback method, instead I'm storing a reference to the client in a list of subscribers. When I need to push data back to the clients I need a reference to the Service object so I can actually utilize do the callback.
Is there a way to publish a Service using WCF that lets you have a reference to the service object? or can I get a reference to the service object from the host object?
Any help would be appreciated...
You can use the singleton pattern in your StringReverser class and pass the instance of it to the ServiceHost constructor:
ServiceHost host = new ServiceHost(
StringReverser.Instance,
new Uri[]{new Uri("net.pipe://localhost")}
);
I agree that Julien's answer is the correct approach, but it is incomplete (at least for .NET 4.5). After you pass in the instance of the service, you have to set the instance context mode for the ServiceHost to Single. If you don't do that, you'll get an error when the ServiceHost Open() method is called.
The way to set the context mode was not at all obvious. Here is a fragment from one of my programs, taken from a different SO answer:
var baseAddress = new Uri("http://localhost:15003/MockGateway");
using (var host = new ServiceHost(new MockGatewayService(), baseAddress))
{
// Since we are passing an instance of the service into ServiceHost (rather
// than passing in the type) we have to set the context mode to single.
var behavior = host.Description.Behaviors.Find<ServiceBehaviorAttribute>();
behavior.InstanceContextMode = InstanceContextMode.Single;
// Continue to use the service here. If you ever need to get a reference
// to the service object you can do so with...
MockGatewayService myService = host.SingletonInstance as MockGatewayService;
// ...
}
in your servicecontract you can call
OperationContext.Current.GetCallbackChannel());
in any method that is called after the service connects, and then pass it out of the service contract. I typically have a join method where I get a guid for the client and grab the callback there. This is harder than it seems. You either need a singleton/global variable to get it out (easy), or you need to make it so that WCF can use parameterized constructors (hard). For the latter, more correct way of doing it, you need to roll your own classes that implement IInstanceProvider and IEndPointBehavior and add your behavior to the endpoint you are interested in. This has the added benefit of allowing you to use different constructors with different endpoints without redefining your contract. There is unfortunately no typesafe way to do this as you will have to use reflection. Sorry I can't provide a sample, but everything I have is proprietary.

Categories

Resources