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;
}
Related
So I have a console application in which I am using Autofac.
I have set up my console application as follows:
I have a class I call ContainerConfig - In this I have all my builder registrations:
public static class ContainerConfig
{
public static IContainer Configure()
{
var builder = new ContainerBuilder();
builder.Register(c => new MatchRun()).As<MatchRun>).SingleInstance();
builder.RegisterType<AuditLogic>().As<IAuditLogic>();
builder.RegisterType<AuditRepository>().As<IAuditRepository>();
builder.RegisterType<ValidationLogic>().As<IValidationLogic>();
return builder.Build();
}
}
I call my main applcation as follows:
private static void Main(string[] args)
{
var container = ContainerConfig.Configure();
using (var scope = container.BeginLifetimeScope())
{
var app = scope.Resolve<IApplication>();
app.Run(args);
}
}
The issue is that I have a connected WCF service. This is my AuditRepository. (FYI - I have not touched WCF for years so I have forgotten most of what I knew).
Its currently constructed to create and dispose of the the proxy each time I make a call to that client. This functions - mostly.
Looks like this:
public string GetStuff(string itemA, string itemB)
{
try
{
GetProxy();
return _expNsProxy.GetStuff(itemA, itemb);
}
catch (Exception ex)
{
IMLogger.Error(ex, ex.Message);
throw ex;
}
finally
{
// CloseProxyConn();
}
}
What i am wondering is can I do this better with Autofac - creating a single instance vs the constant open close - or am I just totally crazy? I know I am not fully asking this the right way - not 100% sure how to actually word the question.
Thanks
The approach to always create a new proxy and close it after each call is good for WCF.
Otherwise you can run into issues. For example if one service call fails the channel created by the proxy goes into a faulted state and you can not do more calls on it just abort it. Then you need to create a new proxy. Also you can have threading issues if you call the same proxy from multiple threads simultaneously.
Check also this documentation with a sample of how to handle errors correctly when calling WCF services.
There is an Autofac.Wcf package that can help you with the creation and freeing of channels. Check the documentation here. It uses the dynamic client generation approach where you just give the interface of your WCF service and it generates the channel based on the interface. This is a bit more low level approach so you will have to understand more what is going on. The generated client class does this for you in the background.
You need two registrations one for the channel factory that is a singleton:
builder
.Register(c => new ChannelFactory<IYourWcfService>(
new BasicHttpBinding(), // here you will have to configure the binding correctly
new EndpointAddress("http://localhost/YourWcfService.svc")))
.SingleInstance();
And a factory registration that will create the channel from the factory every time you request the service:
builder
.Register(c => c.Resolve<ChannelFactory<IYourWcfService>>().CreateChannel())
.As<IIYourWcfService>()
.UseWcfSafeRelease();
We have the following code...
DiscoveryService.cs
[ServiceContract]
public interface IDiscoveryService
{
[OperationContract]
void PrintHello();
}
public class DiscoveryService : IDiscoveryService
{
public void PrintHello()
{
MessageBox.Show("Hello");
}
}
Server.cs
private void InitializeDiscovery()
{
Uri baseAddress = DiscoveryHelper.AvailableTcpBaseAddress;
ServiceHost host = new ServiceHost(typeof (DiscoveryService), baseAddress);
host.EnableDiscovery();
host.Open();
}
Client.cs
private void DiscoverAddressClick(object sender, EventArgs e)
{
EndpointAddress address = DiscoveryHelper.DiscoverAddress<IDiscoveryService>();
Binding binding = new NetTcpBinding();
IDiscoveryService proxy = ChannelFactory<IDiscoveryService>.CreateChannel(binding, address);
proxy.PrintHello();
(proxy as ICommunicationObject).Close();
}
Now, this code seems to work up until the point where we modify anything having to do with the Service Contract, at which point we get a TCP Exception, Error Code 10061 (Connection Actively Refused). However, we cant figure out who is refusing, or why. If we try to revert our code to a previous state, when it was working, we can't, it simply refuses to work after (it seems) we add / remove things from the project.
Every time I build I clean first. We're also following the instructions posted here.
Any thoughts?
I will begin by stating that I'm not all that familiar with WCF Discovery....though I may implement it in our new server stack. But one thing I do not see here is the presence of a proxy server in what you present here.
As I understand it from what I've read on MSDN your WCF service will basically register it's presence to a proxy server running on the network. Your client will then 'discover' your service server via the DiscoveryProxy server. That may be the piece of the plumbing you are missing.
You current implementation of the server will close down the server as soon as the InitializeDiscovery Method is complete because your host only has function scope and will be cleaned up by the garbage collector unless you keep a reference somewhere else.
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.
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.
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.