none
WCF Channels RRS feed

  • Question

  • Hi,

    I have the following scenario:

    IDmmyContract dummy = ChannelFactory<IDmmyContract>.CreateChannel("binding")

    i would like to know whether i can cache dummy for sometime and use it from more than one thread, so does the contract instance holds context information that are not thread-safe?

    What;s the cost of creating a new contract instance per method call?

    Thanks

     

    Thursday, August 17, 2006 9:54 AM

Answers

  • if you want to use same proxy object(create once,reuse for multiple calls) from different threads,that may not be problem,but if multiple threads wants to use same proxy same time,we better to use async communication.

    creating channel for each method may be expensive operation,interface is just to know the shape of the message(soap in/soap out),in above method,we are actually creating channel and we are asking channel to use IDmmyContract.

     

    -Thank you

    Madhu

    Thursday, August 17, 2006 3:24 PM
  • Client Channels are thread-safe. If you need concurrency but are using in-order channels you may want to keep a small pool of channels. If you get the required concurrency with one channel, then that will not be an issue.

    Thursday, August 17, 2006 4:48 PM

All replies

  • if you want to use same proxy object(create once,reuse for multiple calls) from different threads,that may not be problem,but if multiple threads wants to use same proxy same time,we better to use async communication.

    creating channel for each method may be expensive operation,interface is just to know the shape of the message(soap in/soap out),in above method,we are actually creating channel and we are asking channel to use IDmmyContract.

     

    -Thank you

    Madhu

    Thursday, August 17, 2006 3:24 PM
  • Hi,

    To me, this seems closely related to one of my other posted questions.

    In my solution, creating a channel is very expensive, much more so than the actual method call on the service.

    I would really like to see some clear guidance on how to securely keep a proxy around for the life of my app domain.

    Thanks

    Thursday, August 17, 2006 4:04 PM
  • I see what you mean. In my case i have a web-based app, so you recommend for example to create a one channel per client request, so if i have 50 client requests at one time, i will have 50 channels created. The other way is to create one channel per interface, cache it and syncronize it.

    what's the best way to do this, please advice.

    Thanks

    Thursday, August 17, 2006 4:09 PM
  • Client Channels are thread-safe. If you need concurrency but are using in-order channels you may want to keep a small pool of channels. If you get the required concurrency with one channel, then that will not be an issue.

    Thursday, August 17, 2006 4:48 PM
  • Aha, so if i dont have in-order channles it means i dont need concurrency, since channels are thread-safe. That will makes life easier diffenitly.

    Is it ok if i keep the channels cached during the life-time of the ASP.NET application?

    Monday, August 21, 2006 7:53 AM
  • Yes, it's ok to have your channels cached for as long as you like. Note that you should make sure your code is resilient to the cached channel becoming unusable due to idle timeouts, etc. You can detect this by hooking the Faulted event on the channel and/or by wrapping your call in a try/catch (CommunicationException) and then looking at the channel State.
    Tuesday, August 22, 2006 4:53 PM
  • Thank you Kenny,

    Should I expect the Faulted event to fire as soon as the channel times out from being idle?

    Or, will the event only fire if I try to make a call after the timeout?

    I really want to avoid having try/catch blocks that are only there in case of idle timeouts.

    -A

    Tuesday, August 22, 2006 5:04 PM
  • Thanks Kenny, that was really helpfull. One thing that's roaming in my head, is the fact of how would the faulting event implementation go with multi-threading, i mean, there might be one thread accessing the contract while the faulting event is being executed on another thread probably. In this case where would the syncronization go, how can i lock the accessing thread ? would it be ok to go on the interface instance sync block?

    Is there any race condition might happen where one thread start to execute code inside the contract(Inside WCF) and the faulting event is being fired?

    Tuesday, August 22, 2006 5:55 PM
  • In general, any channel call can throw, so if you are writing reliable code then your communication may need to wrap the general I/O area in try/catch (CommunicationException).

    Faulted event will fire as soon as a channel "faults". This is intentionally vague since there are a number of domain-specific reasons why a channel may fault (it detects corruption, security failure, etc). The one thing these domain specific events have in common is that they are events which cause the channel to no longer be usable for I/O. The recovery mechanism in this case is to cleanup (Abort) this channel and create a new one.

    Wednesday, August 23, 2006 7:00 PM
  • Just like anything else, if you have state you need to synchronize then you should do so around any recovery in the Faulted event handler. To be able to advise further I'd love to have a more concrete example of the kind of situation you are imagining, including the resource that you'd be needing to synchronize around.
    Wednesday, August 23, 2006 7:01 PM
  • Thanks for staying on the thread Kenny,

    I'm not trying to handle all communication exceptions, just the inactivity timeout. The errors say "may", so its difficult for me to be sure that  the error is actualy due to the timeout. Is there a minimum value for a timeout? I've set it to 1 second (inactivityTimeout="00:00:01.0")  but that doesn't seem to provoke timeout errors.

    My objective is to avoid the cost of creating a new ChannelFactory<IService> and the instance of IService.

    To do this, I simply keep a copy of the IService instance in a static variable and reuse it indefinitely.

    I've hacked out a class that checks the setting from the bindings and tracks the last use of the static instance. If it gets close to the inactivity timeout, the factory and instance get re-created.

    I don't think this is very elegant, but if I still get the same error that "may" be timeouts then I will know they probably are not timeouts. :S

    private class PersistantInterfaceProxy<T>
    {
     private T _instance;
     private ChannelFactory<T> channelFactory;
     private string _endpointConfigurationName;
     private DateTime lastActivitytime;
     private TimeSpan inactivityTimeout = TimeSpan.Zero;
     public PersistantInterfaceProxy(string endpointConfigurationName)
     {
      _endpointConfigurationName = endpointConfigurationName; 
     }
     public T Instance
     {
      get
      {
       AssureInstance();
       lastActivitytime = DateTime.Now;
       return _instance;
      }
     }
     private void AssureInstance()
     {
      if(_instance ==null)
      {
       RenewInstance(); 
      }
      else
      {
       //One minute before timeout-renew
       if (DateTime.Now.AddMinutes(1).Subtract(lastActivitytime) > inactivityTimeout)
       {
        RenewInstance(); 
       }
      }
     }
     private void RenewInstance()
     {
      this.channelFactory = new ChannelFactory<T>(this._endpointConfigurationName);
      this._instance = this.channelFactory.CreateChannel();
      inactivityTimeout = ((WSFederationHttpBinding)channelFactory.Endpoint.Binding).ReliableSession.InactivityTimeout;
      lastActivitytime = DateTime.Now; 
     }
    }

    Wednesday, August 23, 2006 7:33 PM
  • I have an application where I want to have many threads using the same channel and I need concurrent access since some of the requests can take a long time to process. Creating a new channel for each thread is not an option since my service application maintains a session state that must be accessed across different calls. Passing an application session identifier in each call is not enough because I have a login service and I do not want to force clients to re-login each time a new channel is created. What can I do?

    Friday, August 25, 2006 7:16 AM
  • Note from

    http://msdn2.microsoft.com/en-us/library/aa738757.aspx

    "While channels and clients created by the channels are thread-safe, they might not support writing more than one message to the wire concurrently. If you are sending large messages, particularly if streaming, the send operation might block waiting for another send to complete. This causes two sorts of problems: a lack of concurrency and the possibility of deadlock if the flow of control returns to the service reusing the channel (that is, the shared client calls a service whose code path results in a call back to the shared client). This is true regardless of the type of WCF client you reuse."

    Are you sure you want to only use the same Channel ?

     

    Regards,

     

    Ben

     

     

     

    Wednesday, December 6, 2006 8:17 AM
  • Hi

     

    With this code

     

    Code Snippet

    static void Main(string[] args)

    {

      StoreClient client = new StoreClient("tcp");

     

      client.InnerChannel.Faulted += new EventHandler(InnerChannel_Faulted);

     

      // No problem with this call

      Console.WriteLine("{0} orders retrieved", client.GetOrders("Fabrikam").Length);

     

      Console.Read(); // use this to wait for service to receiveTimeout

     

      Console.WriteLine("1");

      Console.WriteLine("{0} orders retrieved", client.GetOrders"Fabrikam").Length);

      Console.WriteLine("This line is not hit");

      client.Close();

    }

     

    static void InnerChannel_Faulted(object sender, EventArgs e)

    {

      Console.WriteLine("2");

    }

     

     

    If I wait for the service to timeout before I hit a key to pass Console.Read(), I see the event handler called but only after the second call to client.GetOrders, which goes on to throw a CommunicationException.

     

    The output is

     

    4 orders retrieved

     

    1

    2

     

    What should I be doing to recover the channel after the service timeouts?

     

    Friday, August 24, 2007 3:12 PM
  •  

    Use the singleton pattern:

    public class Proxy<T>

        where T : class

        {

    T _MyContractChannel;

    string _Uri;

    ChannelFactory<T> _ChannelFactory;

    object _Implementation;

    InstanceContext _Context;

     

    public Proxy(object pImplementation, string pUri)

    {

        this.Init(pImplementation, pUri);

    }

     

    public T RemoteContract

    {

       get

       {

           if (this._ChannelFactory.State != CommunicationState.Opened)

           {

                this.Init(this._Implementation, this._Uri);

           }

           return this._MyContractChannel;

       }

    }

    public void Init(object pImplementation, string pUri)

    {

        this._Uri = pUri;

        NetTcpBinding lBinding = new NetTcpBinding();

        lBinding.MaxReceivedMessageSize = int.MaxValue;

        lBinding.ReaderQuotas.MaxArrayLength = int.MaxValue;

        EndpointAddress lAddress = new EndpointAddress(this._Uri);

        this._Context = new InstanceContext(pImplementation);

        //lBinding.SendTimeout = lBinding.ReceiveTimeout = TimeSpan.FromMinutes(10); <---- Does not work

        this._ChannelFactory = null;

        if (pImplementation == null)

        {

                this._ChannelFactory = new ChannelFactory<T>(lBinding, lAddress);

        }

        else

        {

               this._ChannelFactory = new DuplexChannelFactory<T>(this._Context, lBinding, lAddress);

        }

        this._MyContractChannel = this._ChannelFactory.CreateChannel();

    }

    }

     

    class Client: IMyCallback{

    public Proxy<IMyContract> _Proxy;

    public static volatile Client _Client;

    public static object _SyncRoot = new object();

     

    private Client()

    {

    this._Proxy =

    new Proxy<IMyContract>(this, "net.tcp://localhost:9001/MyContract");

    }

     

    public void OnCallbackMethod(String pParameter)

    {

    Console.WriteLine(pParameter.ToString());

    }

     

    public static Client Handler

    {

    get

    {

    lock (_SyncRoot)

    {

    if (_Client == null)

    {

    _Client = new Client();

    }

    }

    return _Client;

    }

    }

     

    static void Main(string[] args)

    {

     Client.Handler._Proxy.RemoteContract.MyMethod(); //Thread safe

    }

    }

    Friday, December 14, 2007 7:52 PM
  • Thank Ben,

     

    I am using WS-SecureConversation and the application session is tied to the unique identifier for the SecureConversation. This means I cannot use multiple channels. That said, I did solve my problem by setting the

    ConcurrencyMode to Multiple for my Service contract.

     

    Regards,

     

    Randy

    Friday, December 14, 2007 7:58 PM