Saving a cache object when a WCF webservice is ended - c#

I have a simple WCF webservice with 2 methods : one save/updates an obect in the cache and the other one deletes it. How can I save the object when I close the webservice server.
Using CacheItemRemovedCallback doesn't work because the object is removed everytime i update it.
Using Global.asax.cs.Application_End() doesn't work also because the cache is cleared by the time it get here.
Using Dispose() method method doesn't work because it get called every time a call has finished.
[ServiceContract]
public class WebService
{
[OperationContract]
public void Test(string message)
{
List<string> Logs;
Logs = HttpRuntime.Cache.Get("LogMessages") as List<string>;
if (Logs == null)
{
Logs = new List<string>();
Logs.Add(message);
}
else Logs.Add(message);
HttpRuntime.Cache.Insert("LogMessages", Logs, null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, null);
}
[OperationContract]
public void WriteToFile()
{
List<string> Logs;
Logs = HttpRuntime.Cache.Get("LogMessages") as List<string>;
if (Logs == null)
{
string filename = DateTime.Now.ToString("yyyy_MM_dd_HH_mm_ss_fff");
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
//any method of writing the object to disk
HttpRuntime.Cache.Remove("LogMessages");
});
}
}
}

Generally, if you need to do something when a WCF service starts or stops it should be done by extending the ServiceHostFactory.
Check ServiceHost OnClose or OnClosing event to do what your need.
public class DerivedHost : ServiceHost
{
public DerivedHost( Type t, params Uri baseAddresses ) :
base( t, baseAddresses ) {}
protected override void OnClose(System.TimeSpan timeout)
{
...
base.OnClose(timeout);
}
protected override void OnClosing()
{
...
base.OnClosing();
}
}
You can also implement your own instance provider and use the ReleaseInstance method.
public class MyInstanceProviderBehavior : IInstanceProvider
{
...
#region IInstanceProvider Members
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
...
}
#endregion
}
For more information about WCF extensibility, take a look at Carlos Figueira blog.

Related

Self hosted WCF service global object appears to be newly created every time an endpoint is invoked

Following the tutorial here I'm self hosting a WCF service inside of a windows service. My WCF service contains a global object that I update at regular intervals. I want to serialize that object to JSON and return that JSON string via a service endpoint. When I access the endpoint that calls the serialize method on the service, I get what appears to be a brand new instance of the global. The service is set to [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
I'm instantiating in the same way as the tutorial:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class MyWindowsService: ServiceBase
{public ServiceHost serviceHost = null;
public Service()
{
ServiceName = "MyService";
}
public static void Main()
{
ServiceBase.Run(new MyWindowsService());
}
protected override void OnStart(string[] args)
{
if (serviceHost != null)
{
serviceHost.Close();
}
serviceHost = new ServiceHost(typeof(MyWCFService));
serviceHost.Open();
}
protected override void OnStop()
{
if (serviceHost != null)
{
serviceHost.Close();
serviceHost = null;
}
}
And my WCF service looks like this:
public class MyWCFService: IWCFService
{
private myObject = mySerializableObject;
public MyWCFService()
{
myObject = new MySerializableObject();
myObject.Init();
}
public Stream GetJSON()
{
MemoryStream stream = new MemoryStream();
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(MySerializableObject));
ser.WriteObject(stream, myObject);
string jsonString = Encoding.ASCII.GetString(stream.ToArray());
byte[] resultBytes = Encoding.UTF8.GetBytes(jsonString);
WebOperationContext.Current.OutgoingResponse.ContentType = "text/plain";
return new MemoryStream(resultBytes);
}
}
If I GET the GetJSON endpoint, the string returned is a brand new initialization of the object. If I break on the GetJSON method, myObject shows all newly initialized values. Placing a breakpoint in the MySerializableObject update code shows that the updates are occurring correctly and being saved to the object in memory.
Running the same code in a normal console application works fine. Why the discrepancy? Am I handling the global incorrectly?
Figured it out ... I had a couple of issues going on.
As indicated here, my InstanceContextMode declaration
wasn't decorating the right service, so it was still running in the
default per-call context - hence, a new state object on every connection.
O'Reilly showed me that I wasn't creating my singleton
instance correctly. Side note, when creating a ServiceHost using an instance, you can specify a base URI either explicitly in the constructor or in App.config. That caused me some confusion as many of the examples I ran across passed it in the constructor rather than the config.
Here's my working implementation for posterity:
[ServiceContract(Namespace = "http://my.super.original.namespace")]
public interface IWCFService
{
[OperationContract, WebGet]
Stream GetJSON();
}
//Decorator goes on WCF service, not the Windows service
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class MyWCFService : IWCFService
{
private StateObject _myStateObject;
public MyWCFService()
{
_myStateObject = new StateObject();
_myStateObject.Init();
}
public Stream GetJSON()
{
.
.
.
return "JSON String Here";
}
}
public class MyWindowsService : ServiceBase
{
public ServiceHost serviceHost = null;
private readonly MyWcfService _wcfSingleton;
public MyWindowsService()
{
ServiceName = "WindowsServiceNameHere";
_wcfSingleton = new MyWCFService();
}
public static void Main()
{
ServiceBase.Run(new MyWindowsService());
}
// Start the Windows service.
protected override void OnStart(string[] args)
{
if (serviceHost != null)
{
serviceHost.Close();
}
//load WCF Singleton Instance and open it for connections
serviceHost = new ServiceHost(_wcfSingleton);
serviceHost.Open();
}
protected override void OnStop()
{
if (serviceHost != null)
{
serviceHost.Close();
serviceHost = null;
}
}
}

Using DI to add Interceptor to NHibernate Sessions in legacy code

So, there's a bug in some legacy code I'm maintaining. It causes some mild data corruption, so it's rather serious. I've found the root cause, and have made a sample application that reliable reproduces the bug. I would like to fix it with as little impact on existing applications as possible, but I'm struggling.
The bug lies in the data access layer. More specifically, in how an interceptor is injected into a new Nhibernate Session. The interceptor is used to set a specific entity property when saving or flushing. The property, LoggedInPersonID, is found on nearly all our entities. All entities are generated from CodeSmith templates using the database schema, so the LoggedInPersonID property corresponds to a column that is found on nearly all tables in the database. Together with a couple of other columns and triggers, it is used to keep track of which user created and modified a record in the database. Any transaction that inserts or updates data need to supply a LoggedInPersonID value, or else the transaction will fail.
Whenever a client requires a new session, a call is made to OpenSession in the SessionFactory (not Nhibernate's SessionFactory, but a wrapper). The code below shows the relevant parts of the SessionFactory wrapper class:
public class SessionFactory
{
private ISessionFactory sessionFactory;
private SessionFactory()
{
Init();
}
public static SessionFactory Instance
{
get
{
return Nested.SessionFactory;
}
}
private static readonly object _lock = new object();
public ISession OpenSession()
{
lock (_lock)
{
var beforeInitEventArgs = new SessionFactoryOpenSessionEventArgs(null);
if (BeforeInit != null)
{
BeforeInit(this, beforeInitEventArgs);
}
ISession session;
if (beforeInitEventArgs.Interceptor != null
&& beforeInitEventArgs.Interceptor is IInterceptor)
{
session = sessionFactory.OpenSession(beforeInitEventArgs.Interceptor);
}
else
{
session = sessionFactory.OpenSession();
}
return session;
}
}
private void Init()
{
try
{
var configuration = new Configuration().Configure();
OnSessionFactoryConfiguring(configuration);
sessionFactory = configuration.BuildSessionFactory();
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.Message);
while (ex.InnerException != null)
{
Console.Error.WriteLine(ex.Message);
ex = ex.InnerException;
}
throw;
}
}
private void OnSessionFactoryConfiguring(Configuration configuration)
{
if(SessionFactoryConfiguring != null)
{
SessionFactoryConfiguring(this, new SessionFactoryConfiguringEventArgs(configuration));
}
}
public static event EventHandler<SessionFactoryOpenSessionEventArgs> BeforeInit;
public static event EventHandler<SessionFactoryOpenSessionEventArgs> AfterInit;
public static event EventHandler<SessionFactoryConfiguringEventArgs> SessionFactoryConfiguring;
public class SessionFactoryConfiguringEventArgs : EventArgs
{
public Configuration Configuration { get; private set; }
public SessionFactoryConfiguringEventArgs(Configuration configuration)
{
Configuration = configuration;
}
}
public class SessionFactoryOpenSessionEventArgs : EventArgs
{
private NHibernate.ISession session;
public SessionFactoryOpenSessionEventArgs(NHibernate.ISession session)
{
this.session = session;
}
public NHibernate.ISession Session
{
get
{
return this.session;
}
}
public NHibernate.IInterceptor Interceptor
{
get;
set;
}
}
/// <summary>
/// Assists with ensuring thread-safe, lazy singleton
/// </summary>
private class Nested
{
internal static readonly SessionFactory SessionFactory;
static Nested()
{
try
{
SessionFactory = new SessionFactory();
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
throw;
}
}
}
}
The interceptor is injected through the BeforeInit event. Below is the interceptor implementation:
public class LoggedInPersonIDInterceptor : NHibernate.EmptyInterceptor
{
private int? loggedInPersonID
{
get
{
return this.loggedInPersonIDProvider();
}
}
private Func<int?> loggedInPersonIDProvider;
public LoggedInPersonIDInterceptor(Func<int?> loggedInPersonIDProvider)
{
SetProvider(loggedInPersonIDProvider);
}
public void SetProvider(Func<int?> provider)
{
loggedInPersonIDProvider = provider;
}
public override bool OnFlushDirty(object entity, object id, object[] currentState, object[] previousState,
string[] propertyNames, NHibernate.Type.IType[] types)
{
return SetLoggedInPersonID(currentState, propertyNames);
}
public override bool OnSave(object entity, object id, object[] currentState,
string[] propertyNames, NHibernate.Type.IType[] types)
{
return SetLoggedInPersonID(currentState, propertyNames);
}
protected bool SetLoggedInPersonID(object[] currentState, string[] propertyNames)
{
int max = propertyNames.Length;
var lipid = loggedInPersonID;
for (int i = 0; i < max; i++)
{
if (propertyNames[i].ToLower() == "loggedinpersonid" && currentState[i] == null && lipid.HasValue)
{
currentState[i] = lipid;
return true;
}
}
return false;
}
}
Below is a helper class used by applications to register a BeforeInit event handler:
public static class LoggedInPersonIDInterceptorUtil
{
public static LoggedInPersonIDInterceptor Setup(Func<int?> loggedInPersonIDProvider)
{
var loggedInPersonIdInterceptor = new LoggedInPersonIDInterceptor(loggedInPersonIDProvider);
ShipRepDAL.ShipRepDAO.SessionFactory.BeforeInit += (s, args) =>
{
args.Interceptor = loggedInPersonIdInterceptor;
};
return loggedInPersonIdInterceptor;
}
}
}
The bug is especially prominent in our web services (WCF SOAP). The web services endpoint bindings are all basicHttpBinding. A new Nhibernate session is created for each client request. The LoggedInPersonIDInterceptorUtil.Setup method is called after a client is authenticated, with the authenticated client's ID captured in the closure. Then there's a race to reach code that triggers a call to SessionFactory.OpenSession before another client request registers an event handler to the BeforeInit event with a different closure - because, it's the last handler in the BeforeInit event's invocation list that "wins", potentially returning the wrong interceptor. The bug usually happens when two clients are making requests nearly simultaneously, but also when two clients are calling different web service methods with different execution times (one taking longer from authentication to OpenSession than another).
In addition to the data corruption, there's also a memory leak as the event handlers aren't de-registered? It might be the reason why our web service process is recycled at least once a day?
It really looks like the BeforeInit (and AfterInit) events need to go. I could alter the signature of the OpenSession method, and add an IInterceptor parameter. But this would break a lot of code, and I don't want to pass in an interceptor whenever a session is retrieved - I would like this to be transparent. Since the interceptor is a cross cutting concern in all applications using the DAL, would dependency injection be a viable solution? Unity is used in some other areas of our applications.
Any nudge in the right direction would be greatly appreciated :)
Instead of supplying the interceptor at each ISessionFactory.OpenSession call, I would use a single interceptor instance globally configured (Configuration.SetInterceptor()).
This instance would retrieve the data to use from an adequate context allowing to isolate this data per request/user/whatever suits the application.
(System.ServiceModel.OperationContext, System.Web.HttpContext, ..., depending on the application kind.)
The context data in your case would be set where LoggedInPersonIDInterceptorUtil.Setup is currently called.
If you need to use the same interceptor implementation for applications requiring different contextes, then you will need to choose the context to use according to some configuration parameter you would add (or inject it as a dependency in your interceptor).
Dependency Injection example:
DependencyInjectionInterceptor.cs:
using NHibernate;
using System;
using Microsoft.Extensions.DependencyInjection;
namespace MyAmazingApplication
{
public class DependencyInjectionInterceptor : EmptyInterceptor
{
private readonly IServiceProvider _serviceProvider;
public DependencyInjectionInterceptor(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public T GetService<T>() => _serviceProvider.GetService<T>();
public T GetRequiredService<T>() => _serviceProvider.GetRequiredService<T>();
}
}
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
...
var cfg = new Configuration();
... // your config setup
cfg.SetListeners(NHibernate.Event.ListenerType.PreInsert, new[] { new AuditEventListener() });
cfg.SetListeners(NHibernate.Event.ListenerType.PreUpdate, new[] { new AuditEventListener() });
services.AddSingleton(cfg);
services.AddSingleton(s => s.GetRequiredService<Configuration>().BuildSessionFactory());
services.AddScoped(s => s.GetRequiredService<ISessionFactory>().WithOptions().Interceptor(new DependencyInjectionInterceptor(s)).OpenSession());
... // you other services setup
}
AuditEventListener.cs:
public class AuditEventListener : IPreUpdateEventListener, IPreInsertEventListener
{
public bool OnPreUpdate(PreUpdateEvent e)
{
var user = ((DependencyInjectionInterceptor)e.Session.Interceptor).GetService<ICurrentUser>();
if (e.Entity is IEntity)
UpdateAuditTrail(user, e.State, e.Persister.PropertyNames, (IEntity)e.Entity, false);
return false;
}
}
So you use interceptor to get your scoped or any other service:
var myService = ((DependencyInjectionInterceptor)e.Session.Interceptor).GetService<IService>();
ICurrentUser in particular is a scoped service which uses HttpContext to get the current user.
I hope it might be helpful for everyone.

Why does a Web API TypeFormatter not run in the same Call Context as the request of the request?

When handling a request, Web API allows the complete operation to run in a single Context to flow with the asynchronous code we write in Web API. Somehow however, when creating custom a MediaTypeFormatter, Web API doesn't use the same context when executing a type formatter.
My question is: Why is that and how can we circumvent this?
Take a look at the following code:
public static class CallContextHelper
{
private static readonly string key = Guid.NewGuid().ToString();
public static void Append(string text) {
var appendedText = (string)CallContext.LogicalGetData(key) + " -> " + text;
CallContext.LogicalSetData(key, appendedText);
Debug.WriteLine(appendedText);
}
}
public sealed class TestDependencyResolver : IDependencyResolver
{
void IDisposable.Dispose() { }
IDependencyScope IDependencyResolver.BeginScope() {
CallContextHelper.Append("IDependencyResolver.BeginScope");
return this;
}
object IDependencyScope.GetService(Type serviceType) => null;
IEnumerable<object> IDependencyScope.GetServices(Type serviceType) =>
Enumerable.Empty<object>();
}
public class ValuesController : ApiController
{
public ValuesController() {
CallContextHelper.Append("ValuesController.ctor");
}
public IEnumerable<string> Get() {
CallContextHelper.Append("ValuesController.Get");
return new string[] { "value1", "value2" };
}
}
With the following custom media type formatter:
public class MyCustomTypeFormatter : XmlMediaTypeFormatter
{
public MyCustomTypeFormatter()
{
SupportedMediaTypes.Add(
new MediaTypeHeaderValue("application/vnd.mywebapplication+json"));
}
public override bool CanReadType(Type type) => false;
public override bool CanWriteType(Type type) => true;
public override Task WriteToStreamAsync(Type type, object value, Stream ws,
HttpContent content, System.Net.TransportContext tc)
{
CallContextHelper.Append("WriteToStreamAsync (begin)");
return base.WriteToStreamAsync(type, value, ws, content, tc)
.ContinueWith(continuation =>
{
CallContextHelper.Append("WriteToStreamAsync (continu)");
});
}
}
Bootstrapper:
GlobalConfiguration.Configuration.DependencyResolver = new TestDependencyResolver();
GlobalConfiguration.Configuration.Formatters.Insert(0, new MyCustomTypeFormatter());
If we run the application and call the ValueController, we see the following output:
-> IDependencyResolver.BeginScope
-> IDependencyResolver.BeginScope -> ValuesController.ctor
-> IDependencyResolver.BeginScope -> ValuesController.ctor -> ValuesController.Get
-> WriteToStreamAsync (begin)
-> WriteToStreamAsync (begin) -> WriteToStreamAsync (continuewith)
In other words, what you see is that the call to IDependencyResolver.BeginScope, the call to the constructor of the ValuesConstructor and the ValuesController.Get method operate in the same logical call context, but the calls to the TypeFormatter operate in their own call context.
What I'm trying to do is allow everything to run in a single scope, to allow reusing the same unit of work instance throughout the request. There's no HttpRequestMessage passed into the TypeFormatter, so there seems no other way to get this running in a single context.

How to get the invoked operation name within a IClientMessageInspector?

I implemented an IClientMessageInspector to "intercept" outgoing web service call in my application. Is it possible to find out which operation is being called from inside the BeforeSendRequest and AfterReceiveReply?
There is a similar question here, How do i get the invoked operation name within a WCF Message Inspector, which is for the server side (the side receiving the request). I tried to do something similar, e.g.
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
var v = OperationContext.Current.OutgoingMessageProperties["HttpOperationName"];
return null;
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
var v = OperationContext.Current.OutgoingMessageProperties["HttpOperationName"];
}
but during outgoing request it seems that OperationContext.Current is null, so I cannot use this. Any idea how to get it? Any idea how to do it cleanly (as opposed, to say, parse the SOAP xml)?
From the comments you asked how doing this could be done with IParameterInspector. The operation name is part of the Before/AfterCall methods.
Just to add to my comments on which inspector to use. From Carlos Figueira's blogs:
The message inspectors, described in the previous post of this series,
allows you complete control over the message going through the WCF
stack. They’re very powerful, but you have to know how to deal with
the Message object, which is not the most desirable way of
programming. If the service model in WCF hides all the messaging
framework by allowing us to define our services in terms of
strongly-typed operations (i.e., using nice primitive and user defined
types), there should be a way of intercepting requests / responses
after all the processing to extract those parameters from incoming
messages (or before they’re packaged in outgoing messages) is done.
The IParameterInspector is exactly that – before and after each call,
the inspector gets a chance to inspect the operation inputs, outputs
and return value, in the same types as defined by the operation
contract, no conversion needed (the only thing needed is a cast, since
the parameters are passed as objects).
This is a complete command line program that demonstrates:
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
namespace WCFClientInspector
{
public class OperationLogger : IParameterInspector
{
public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
{
Console.WriteLine("Completed operation:" + operationName);
}
public object BeforeCall(string operationName, object[] inputs)
{
Console.WriteLine("Calling operation:" + operationName);
return null;
}
}
public class OperationLoggerEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
foreach (ClientOperation operation in clientRuntime.ClientOperations)
{
operation.ClientParameterInspectors.Add(new OperationLogger());
}
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
[ServiceContract]
public interface ISimple
{
[OperationContract]
void DoSomthing(string s);
}
public class SimpleService : ISimple
{
public void DoSomthing(string s)
{
Console.WriteLine("Called:" + s);
}
}
public static class AttributesAndContext
{
static void Main(string[] args)
{
ServiceHost simpleHost = new ServiceHost(typeof(SimpleService), new Uri("http://localhost/Simple"));
simpleHost.Open();
ChannelFactory<ISimple> factory = new ChannelFactory<ISimple>(simpleHost.Description.Endpoints[0]);
factory.Endpoint.EndpointBehaviors.Add(new OperationLoggerEndpointBehavior());
ISimple proxy = factory.CreateChannel();
proxy.DoSomthing("hi");
Console.WriteLine("Press ENTER to close the host.");
Console.ReadLine();
((ICommunicationObject)proxy).Shutdown();
simpleHost.Shutdown();
}
}
public static class Extensions
{
static public void Shutdown(this ICommunicationObject obj)
{
try
{
obj.Close();
}
catch (Exception ex)
{
Console.WriteLine("Shutdown exception: {0}", ex.Message);
obj.Abort();
}
}
}
}
It should give the output:
Calling operation:DoSomthing
Called:hi
Completed operation:DoSomthing
Press ENTER to close the host.
What about reply.Headers.Action and request.Headers.Action. Of course the rest is same tricky as in question linked. So the full code will be:
var action = reply.Headers.Action.Substring(reply.Headers.Action.LastIndexOf("/", StringComparison.OrdinalIgnoreCase) + 1);
or
var action = request.Headers.Action.Substring(request.Headers.Action.LastIndexOf("/", StringComparison.OrdinalIgnoreCase) + 1);

Why do I get a WCF timeout even though my service call and callback are successful?

I'm playing around with hooking up an in-game console to a WCF interface, so an external application can send console commands and receive console output. To accomplish this I created the following service contracts:
public interface IConsoleNetworkCallbacks
{
[OperationContract(IsOneWay = true)]
void NewOutput(IEnumerable<string> text, string category);
}
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IConsoleNetworkCallbacks))]
public interface IConsoleInterface
{
[OperationContract]
void ProcessInput(string input);
[OperationContract]
void ChangeCategory(string category);
}
On the server I implemented it with:
public class ConsoleNetworkInterface : IConsoleInterface, IDisposable
{
public ConsoleNetworkInterface()
{
ConsoleManager.Instance.RegisterOutputUpdateHandler(OutputHandler);
}
public void Dispose()
{
ConsoleManager.Instance.UnregisterOutputHandler(OutputHandler);
}
public void ProcessInput(string input)
{
ConsoleManager.Instance.ProcessInput(input);
}
public void ChangeCategory(string category)
{
ConsoleManager.Instance.UnregisterOutputHandler(OutputHandler);
ConsoleManager.Instance.RegisterOutputUpdateHandler(OutputHandler, category);
}
protected void OutputHandler(IEnumerable<string> text, string category)
{
var callbacks = OperationContext.Current.GetCallbackChannel<IConsoleNetworkCallbacks>();
callbacks.NewOutput(text, category);
}
}
On the client I implemented the callback with:
public class Callbacks : IConsoleNetworkCallbacks
{
public void NewOutput(IEnumerable<string> text, string category)
{
MessageBox.Show(string.Format("{0} lines received for '{1}' category", text.Count(), category));
}
}
Finally, I establish the service host with the following class:
public class ConsoleServiceHost : IDisposable
{
protected ServiceHost _host;
public ConsoleServiceHost()
{
_host = new ServiceHost(typeof(ConsoleNetworkInterface), new Uri[] { new Uri("net.pipe://localhost") });
_host.AddServiceEndpoint(typeof(IConsoleInterface), new NetNamedPipeBinding(), "FrbConsolePipe");
_host.Open();
}
public void Dispose()
{
_host.Close();
}
}
and use the following code on my client to establish the connection:
protected Callbacks _callbacks;
protected IConsoleInterface _proxy;
protected void ConnectToConsoleServer()
{
_callbacks = new Callbacks();
var factory = new DuplexChannelFactory<IConsoleInterface>(_callbacks,
new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/FrbConsolePipe"));
_proxy = factory.CreateChannel();
_proxy.ProcessInput("Connected");
}
So what happens is that my ConnectToConsoleServer() is called and then it gets all the way to _proxy.ProcessInput("Connected");. In my game (on the server) I immediately see the output caused by the ProcessInput call, but the client is still stalled on the _proxy.ProcessInput() call.
After a minute my client gets a JIT TimeoutException however at the same time my MessageBox message appears.
So obviously not only is my command being sent immediately, my callback is being correctly called. So why am I getting a timeout exception?
Note: Even removing the MessageBox call, I still have this issue, so it's not an issue of the GUI blocking the callback response.
You need to specify CallbackBehavior for your client class implementing the Callback interface
[CallbackBehavior(ConcurrencyMode=ConcurrencyMode.Multiple, UseSynchronizationContext=false)]
See the following for a better explanation
http://www.switchonthecode.com/tutorials/wcf-callbacks-hanging-wpf-applications
It's possible that it's your _proxy.ProcessInput("Connected") which is blocking the call - this is consistent with your timeout experience, as the server sends the response immediately, but the client can't receive it as it's stuck on "ProcessInput". When your call finally times out, the blocking call terminates, at which point the callback completes.
To verify this, could you try invoking using this (non blocking) call instead?
((Action)(() => _proxy.ProcessInput("Connected"))).BeginInvoke(null, null);

Categories