1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //------------------------------------------------------------
5 namespace System.ServiceModel
7 using System.ComponentModel;
8 using System.Diagnostics;
10 using System.ServiceModel.Channels;
11 using System.ServiceModel.Description;
12 using System.ServiceModel.Diagnostics;
13 using System.Threading;
14 using System.ServiceModel.Diagnostics.Application;
15 using System.Runtime.Remoting.Messaging;
16 using System.ServiceModel.Dispatcher;
17 using System.Security;
19 public abstract class ClientBase<TChannel> : ICommunicationObject, IDisposable
20 where TChannel : class
23 ChannelFactoryRef<TChannel> channelFactoryRef;
24 EndpointTrait<TChannel> endpointTrait;
26 // Determine whether the proxy can share factory with others. It is false only if the public getters
28 bool canShareFactory = true;
30 // Determine whether the proxy is currently holding a cached factory
31 bool useCachedFactory;
33 // Determine whether we have locked down sharing for this proxy. This is turned on only when the channel
35 bool sharingFinalized;
37 // Determine whether the ChannelFactoryRef has been released. We should release it only once per proxy
38 bool channelFactoryRefReleased;
40 // Determine whether we have released the last ref count of the ChannelFactory so that we could abort it when it was closing.
43 object syncRoot = new object();
45 object finalizeLock = new object();
47 // Cache at most 32 ChannelFactories
48 const int maxNumChannelFactories = 32;
49 static ChannelFactoryRefCache<TChannel> factoryRefCache = new ChannelFactoryRefCache<TChannel>(maxNumChannelFactories);
50 static object staticLock = new object();
52 static object cacheLock = new object();
53 static CacheSetting cacheSetting = CacheSetting.Default;
54 static bool isCacheSettingReadOnly;
56 static AsyncCallback onAsyncCallCompleted = Fx.ThunkCallback(new AsyncCallback(OnAsyncCallCompleted));
58 // IMPORTANT: any changes to the set of protected .ctors of this class need to be reflected
59 // in ServiceContractGenerator.cs as well.
61 protected ClientBase()
63 MakeCacheSettingReadOnly();
65 if (cacheSetting == CacheSetting.AlwaysOff)
67 this.channelFactoryRef = new ChannelFactoryRef<TChannel>(new ChannelFactory<TChannel>("*"));
68 this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
73 this.endpointTrait = new ConfigurationEndpointTrait<TChannel>("*", null, null);
74 InitializeChannelFactoryRef();
78 protected ClientBase(string endpointConfigurationName)
80 if (endpointConfigurationName == null)
81 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointConfigurationName");
83 MakeCacheSettingReadOnly();
85 if (cacheSetting == CacheSetting.AlwaysOff)
87 this.channelFactoryRef = new ChannelFactoryRef<TChannel>(new ChannelFactory<TChannel>(endpointConfigurationName));
88 this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
93 this.endpointTrait = new ConfigurationEndpointTrait<TChannel>(endpointConfigurationName, null, null);
94 InitializeChannelFactoryRef();
98 protected ClientBase(string endpointConfigurationName, string remoteAddress)
100 if (endpointConfigurationName == null)
101 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointConfigurationName");
102 if (remoteAddress == null)
103 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("remoteAddress");
105 MakeCacheSettingReadOnly();
106 EndpointAddress endpointAddress = new EndpointAddress(remoteAddress);
108 if (cacheSetting == CacheSetting.AlwaysOff)
110 this.channelFactoryRef = new ChannelFactoryRef<TChannel>(new ChannelFactory<TChannel>(endpointConfigurationName, endpointAddress));
111 this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
116 this.endpointTrait = new ConfigurationEndpointTrait<TChannel>(endpointConfigurationName, endpointAddress, null);
117 InitializeChannelFactoryRef();
121 protected ClientBase(string endpointConfigurationName, EndpointAddress remoteAddress)
123 if (endpointConfigurationName == null)
124 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointConfigurationName");
125 if (remoteAddress == null)
126 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("remoteAddress");
128 MakeCacheSettingReadOnly();
130 if (cacheSetting == CacheSetting.AlwaysOff)
132 this.channelFactoryRef = new ChannelFactoryRef<TChannel>(new ChannelFactory<TChannel>(endpointConfigurationName, remoteAddress));
133 this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
138 this.endpointTrait = new ConfigurationEndpointTrait<TChannel>(endpointConfigurationName, remoteAddress, null);
139 InitializeChannelFactoryRef();
144 protected ClientBase(Binding binding, EndpointAddress remoteAddress)
147 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("binding");
148 if (remoteAddress == null)
149 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("remoteAddress");
151 MakeCacheSettingReadOnly();
153 if (cacheSetting == CacheSetting.AlwaysOn)
155 this.endpointTrait = new ProgrammaticEndpointTrait<TChannel>(binding, remoteAddress, null);
156 InitializeChannelFactoryRef();
160 this.channelFactoryRef = new ChannelFactoryRef<TChannel>(new ChannelFactory<TChannel>(binding, remoteAddress));
161 this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
166 protected ClientBase(ServiceEndpoint endpoint)
168 if (endpoint == null)
169 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpoint");
171 MakeCacheSettingReadOnly();
173 if (cacheSetting == CacheSetting.AlwaysOn)
175 this.endpointTrait = new ServiceEndpointTrait<TChannel>(endpoint, null);
176 this.InitializeChannelFactoryRef();
180 channelFactoryRef = new ChannelFactoryRef<TChannel>(new ChannelFactory<TChannel>(endpoint));
181 channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
186 protected ClientBase(InstanceContext callbackInstance)
188 if (callbackInstance == null)
189 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("callbackInstance");
191 MakeCacheSettingReadOnly();
193 if (cacheSetting == CacheSetting.AlwaysOff)
195 this.channelFactoryRef = new ChannelFactoryRef<TChannel>(
196 new DuplexChannelFactory<TChannel>(callbackInstance, "*"));
197 this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
202 this.endpointTrait = new ConfigurationEndpointTrait<TChannel>("*", null, callbackInstance);
203 InitializeChannelFactoryRef();
207 protected ClientBase(InstanceContext callbackInstance, string endpointConfigurationName)
209 if (callbackInstance == null)
210 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("callbackInstance");
211 if (endpointConfigurationName == null)
212 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointConfigurationName");
214 MakeCacheSettingReadOnly();
216 if (cacheSetting == CacheSetting.AlwaysOff)
218 this.channelFactoryRef = new ChannelFactoryRef<TChannel>(
219 new DuplexChannelFactory<TChannel>(callbackInstance, endpointConfigurationName));
220 this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
225 this.endpointTrait = new ConfigurationEndpointTrait<TChannel>(endpointConfigurationName, null, callbackInstance);
226 InitializeChannelFactoryRef();
230 protected ClientBase(InstanceContext callbackInstance, string endpointConfigurationName, string remoteAddress)
232 if (callbackInstance == null)
233 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("callbackInstance");
234 if (endpointConfigurationName == null)
235 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointConfigurationName");
236 if (remoteAddress == null)
237 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("remoteAddress");
239 MakeCacheSettingReadOnly();
240 EndpointAddress endpointAddress = new EndpointAddress(remoteAddress);
242 if (cacheSetting == CacheSetting.AlwaysOff)
244 this.channelFactoryRef = new ChannelFactoryRef<TChannel>(
245 new DuplexChannelFactory<TChannel>(callbackInstance, endpointConfigurationName, endpointAddress));
246 this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
251 this.endpointTrait = new ConfigurationEndpointTrait<TChannel>(endpointConfigurationName, endpointAddress, callbackInstance);
252 InitializeChannelFactoryRef();
256 protected ClientBase(InstanceContext callbackInstance, string endpointConfigurationName, EndpointAddress remoteAddress)
258 if (callbackInstance == null)
259 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("callbackInstance");
260 if (endpointConfigurationName == null)
261 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointConfigurationName");
262 if (remoteAddress == null)
263 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("remoteAddress");
265 MakeCacheSettingReadOnly();
267 if (cacheSetting == CacheSetting.AlwaysOff)
269 this.channelFactoryRef = new ChannelFactoryRef<TChannel>(
270 new DuplexChannelFactory<TChannel>(callbackInstance, endpointConfigurationName, remoteAddress));
271 this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
276 this.endpointTrait = new ConfigurationEndpointTrait<TChannel>(endpointConfigurationName, remoteAddress, callbackInstance);
277 InitializeChannelFactoryRef();
281 protected ClientBase(InstanceContext callbackInstance, Binding binding, EndpointAddress remoteAddress)
283 if (callbackInstance == null)
284 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("callbackInstance");
286 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("binding");
287 if (remoteAddress == null)
288 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("remoteAddress");
290 MakeCacheSettingReadOnly();
292 if (cacheSetting == CacheSetting.AlwaysOn)
294 this.endpointTrait = new ProgrammaticEndpointTrait<TChannel>(binding, remoteAddress, callbackInstance);
295 InitializeChannelFactoryRef();
299 this.channelFactoryRef = new ChannelFactoryRef<TChannel>(
300 new DuplexChannelFactory<TChannel>(callbackInstance, binding, remoteAddress));
301 this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
306 protected ClientBase(InstanceContext callbackInstance, ServiceEndpoint endpoint)
308 if (callbackInstance == null)
309 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("callbackInstance");
310 if (endpoint == null)
311 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpoint");
313 MakeCacheSettingReadOnly();
315 if (cacheSetting == CacheSetting.AlwaysOn)
317 this.endpointTrait = new ServiceEndpointTrait<TChannel>(endpoint, callbackInstance);
318 InitializeChannelFactoryRef();
322 this.channelFactoryRef = new ChannelFactoryRef<TChannel>(
323 new DuplexChannelFactory<TChannel>(callbackInstance, endpoint));
324 this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
329 protected T GetDefaultValueForInitialization<T>()
342 protected TChannel Channel
346 // created on demand, so that Mort can modify .Endpoint before calling methods on the client
347 if (this.channel == null)
351 if (this.channel == null)
353 using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.CreateBoundedActivity() : null)
355 if (DiagnosticUtility.ShouldUseActivity)
357 ServiceModelActivity.Start(activity, SR.GetString(SR.ActivityOpenClientBase, typeof(TChannel).FullName), ActivityType.OpenClient);
360 if (this.useCachedFactory)
364 CreateChannelInternal();
366 #pragma warning suppress 56500 // covered by FxCOP
369 if (this.useCachedFactory &&
370 (ex is CommunicationException ||
371 ex is ObjectDisposedException ||
372 ex is TimeoutException))
374 DiagnosticUtility.TraceHandledException(ex, TraceEventType.Warning);
375 InvalidateCacheAndCreateChannel();
379 #pragma warning suppress 56503 // [....], We throw only for unknown exceptions.
386 CreateChannelInternal();
396 public static CacheSetting CacheSetting
406 if (isCacheSettingReadOnly && cacheSetting != value)
408 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxImmutableClientBaseCacheSetting, typeof(TChannel).ToString())));
412 cacheSetting = value;
418 public ChannelFactory<TChannel> ChannelFactory
422 if (cacheSetting == CacheSetting.Default)
426 return GetChannelFactory();
430 public ClientCredentials ClientCredentials
434 if (cacheSetting == CacheSetting.Default)
438 return this.ChannelFactory.Credentials;
442 public CommunicationState State
446 IChannel channel = (IChannel)this.channel;
449 return channel.State;
453 // we may have failed to create the channel under open, in which case we our factory wouldn't be open
454 if (!this.useCachedFactory)
456 return GetChannelFactory().State;
460 return CommunicationState.Created;
466 public IClientChannel InnerChannel
470 return (IClientChannel)Channel;
474 public ServiceEndpoint Endpoint
478 if (cacheSetting == CacheSetting.Default)
482 return GetChannelFactory().Endpoint;
488 ((ICommunicationObject)this).Open(GetChannelFactory().InternalOpenTimeout);
493 IChannel channel = (IChannel)this.channel;
499 if (!channelFactoryRefReleased)
503 if (!channelFactoryRefReleased)
505 if (this.channelFactoryRef.Release())
507 this.releasedLastRef = true;
510 channelFactoryRefReleased = true;
515 // Abort the ChannelFactory if we released the last one. We should be able to abort it when another thread is closing it.
516 if (this.releasedLastRef)
518 this.channelFactoryRef.Abort();
524 ((ICommunicationObject)this).Close(GetChannelFactory().InternalCloseTimeout);
527 public void DisplayInitializationUI()
529 ((IClientChannel)this.InnerChannel).DisplayInitializationUI();
532 // This ensures that the cachesetting (on, off or default) cannot be modified by
533 // another ClientBase instance of matching TChannel after the first instance is created.
534 void MakeCacheSettingReadOnly()
536 if (isCacheSettingReadOnly)
541 isCacheSettingReadOnly = true;
545 void CreateChannelInternal()
549 this.channel = this.CreateChannel();
550 if (this.sharingFinalized)
552 if (this.canShareFactory && !this.useCachedFactory)
554 // It is OK to add ChannelFactory to the cache now.
555 TryAddChannelFactoryToCache();
561 if (!this.sharingFinalized && cacheSetting == CacheSetting.Default)
563 // this.CreateChannel() is not called. For safety, we disable sharing.
569 protected virtual TChannel CreateChannel()
571 if (this.sharingFinalized)
572 return GetChannelFactory().CreateChannel();
574 lock (this.finalizeLock)
576 this.sharingFinalized = true;
577 return GetChannelFactory().CreateChannel();
581 void IDisposable.Dispose()
586 void ICommunicationObject.Open(TimeSpan timeout)
588 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
589 if (!this.useCachedFactory)
591 GetChannelFactory().Open(timeoutHelper.RemainingTime());
594 this.InnerChannel.Open(timeoutHelper.RemainingTime());
597 void ICommunicationObject.Close(TimeSpan timeout)
599 using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.CreateBoundedActivity() : null)
601 if (DiagnosticUtility.ShouldUseActivity)
603 ServiceModelActivity.Start(activity, SR.GetString(SR.ActivityCloseClientBase, typeof(TChannel).FullName), ActivityType.Close);
605 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
607 if (this.channel != null)
609 InnerChannel.Close(timeoutHelper.RemainingTime());
612 if (!channelFactoryRefReleased)
616 if (!channelFactoryRefReleased)
618 if (this.channelFactoryRef.Release())
620 this.releasedLastRef = true;
623 this.channelFactoryRefReleased = true;
627 // Close the factory outside of the lock so that we can abort from a different thread.
628 if (this.releasedLastRef)
630 if (this.useCachedFactory)
632 this.channelFactoryRef.Abort();
636 this.channelFactoryRef.Close(timeoutHelper.RemainingTime());
643 event EventHandler ICommunicationObject.Closed
647 this.InnerChannel.Closed += value;
651 this.InnerChannel.Closed -= value;
655 event EventHandler ICommunicationObject.Closing
659 this.InnerChannel.Closing += value;
663 this.InnerChannel.Closing -= value;
667 event EventHandler ICommunicationObject.Faulted
671 this.InnerChannel.Faulted += value;
675 this.InnerChannel.Faulted -= value;
679 event EventHandler ICommunicationObject.Opened
683 this.InnerChannel.Opened += value;
687 this.InnerChannel.Opened -= value;
691 event EventHandler ICommunicationObject.Opening
695 this.InnerChannel.Opening += value;
699 this.InnerChannel.Opening -= value;
703 IAsyncResult ICommunicationObject.BeginClose(AsyncCallback callback, object state)
705 return ((ICommunicationObject)this).BeginClose(GetChannelFactory().InternalCloseTimeout, callback, state);
708 IAsyncResult ICommunicationObject.BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
710 return new ChainedAsyncResult(timeout, callback, state, BeginChannelClose, EndChannelClose, BeginFactoryClose, EndFactoryClose);
713 void ICommunicationObject.EndClose(IAsyncResult result)
715 ChainedAsyncResult.End(result);
718 IAsyncResult ICommunicationObject.BeginOpen(AsyncCallback callback, object state)
720 return ((ICommunicationObject)this).BeginOpen(GetChannelFactory().InternalOpenTimeout, callback, state);
723 IAsyncResult ICommunicationObject.BeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
725 return new ChainedAsyncResult(timeout, callback, state, BeginFactoryOpen, EndFactoryOpen, BeginChannelOpen, EndChannelOpen);
728 void ICommunicationObject.EndOpen(IAsyncResult result)
730 ChainedAsyncResult.End(result);
733 //ChainedAsyncResult methods for opening and closing ChannelFactory<T>
735 internal IAsyncResult BeginFactoryOpen(TimeSpan timeout, AsyncCallback callback, object state)
737 if (this.useCachedFactory)
739 return new CompletedAsyncResult(callback, state);
743 return GetChannelFactory().BeginOpen(timeout, callback, state);
747 internal void EndFactoryOpen(IAsyncResult result)
749 if (this.useCachedFactory)
751 CompletedAsyncResult.End(result);
755 GetChannelFactory().EndOpen(result);
759 internal IAsyncResult BeginChannelOpen(TimeSpan timeout, AsyncCallback callback, object state)
761 return this.InnerChannel.BeginOpen(timeout, callback, state);
764 internal void EndChannelOpen(IAsyncResult result)
766 this.InnerChannel.EndOpen(result);
769 internal IAsyncResult BeginFactoryClose(TimeSpan timeout, AsyncCallback callback, object state)
771 if (this.useCachedFactory)
773 return new CompletedAsyncResult(callback, state);
777 return GetChannelFactory().BeginClose(timeout, callback, state);
781 internal void EndFactoryClose(IAsyncResult result)
783 if (typeof(CompletedAsyncResult).IsAssignableFrom(result.GetType()))
785 CompletedAsyncResult.End(result);
789 GetChannelFactory().EndClose(result);
793 internal IAsyncResult BeginChannelClose(TimeSpan timeout, AsyncCallback callback, object state)
795 if (this.channel != null)
797 return this.InnerChannel.BeginClose(timeout, callback, state);
801 return new CompletedAsyncResult(callback, state);
805 internal void EndChannelClose(IAsyncResult result)
807 if (typeof(CompletedAsyncResult).IsAssignableFrom(result.GetType()))
809 CompletedAsyncResult.End(result);
813 this.InnerChannel.EndClose(result);
817 ChannelFactory<TChannel> GetChannelFactory()
819 return this.channelFactoryRef.ChannelFactory;
822 void InitializeChannelFactoryRef()
824 Fx.Assert(this.channelFactoryRef == null, "The channelFactory should have never been assigned");
825 Fx.Assert(this.canShareFactory, "GetChannelFactoryFromCache can be called only when canShareFactory is true");
828 ChannelFactoryRef<TChannel> factoryRef;
829 if (factoryRefCache.TryGetValue(this.endpointTrait, out factoryRef))
831 if (factoryRef.ChannelFactory.State != CommunicationState.Opened)
833 // Remove the bad ChannelFactory.
834 factoryRefCache.Remove(this.endpointTrait);
838 this.channelFactoryRef = factoryRef;
839 this.channelFactoryRef.AddRef();
840 useCachedFactory = true;
841 if (TD.ClientBaseChannelFactoryCacheHitIsEnabled())
843 TD.ClientBaseChannelFactoryCacheHit(this);
850 if (this.channelFactoryRef == null)
852 // Creating the ChannelFactory at initial time to catch configuration exception earlier.
853 this.channelFactoryRef = CreateChannelFactoryRef(this.endpointTrait);
857 static ChannelFactoryRef<TChannel> CreateChannelFactoryRef(EndpointTrait<TChannel> endpointTrait)
859 Fx.Assert(endpointTrait != null, "The endpointTrait should not be null when the factory can be shared.");
861 ChannelFactory<TChannel> channelFactory = endpointTrait.CreateChannelFactory();
862 channelFactory.TraceOpenAndClose = false;
863 return new ChannelFactoryRef<TChannel>(channelFactory);
866 // Once the channel is created, we can't disable caching.
867 // This method can be called safely multiple times.
868 // this.sharingFinalized is set the first time the method is called.
869 // Subsequent calls are essentially no-ops.
870 void TryDisableSharing()
872 if (this.sharingFinalized)
875 lock (this.finalizeLock)
877 if (this.sharingFinalized)
880 this.canShareFactory = false;
881 this.sharingFinalized = true;
883 if (this.useCachedFactory)
885 ChannelFactoryRef<TChannel> pendingFactoryRef = this.channelFactoryRef;
886 this.channelFactoryRef = CreateChannelFactoryRef(this.endpointTrait);
887 this.useCachedFactory = false;
891 if (!pendingFactoryRef.Release())
893 pendingFactoryRef = null;
897 if (pendingFactoryRef != null)
898 pendingFactoryRef.Abort();
902 // can be done outside the lock since the lines below do not access shared data.
903 // also the use of this.sharingFinalized in the lines above ensures that tracing
904 // happens only once and only when needed.
905 if (TD.ClientBaseUsingLocalChannelFactoryIsEnabled())
907 TD.ClientBaseUsingLocalChannelFactory(this);
911 void TryAddChannelFactoryToCache()
913 Fx.Assert(this.canShareFactory, "This should be called only when this proxy can share ChannelFactory.");
914 Fx.Assert(this.channelFactoryRef.ChannelFactory.State == CommunicationState.Opened,
915 "The ChannelFactory must be in Opened state for caching.");
917 // Lock the cache and add the item to synchronize with lookup.
920 ChannelFactoryRef<TChannel> cfRef;
921 if (!factoryRefCache.TryGetValue(this.endpointTrait, out cfRef))
923 // Increment the ref count before adding to the cache.
924 this.channelFactoryRef.AddRef();
925 factoryRefCache.Add(this.endpointTrait, this.channelFactoryRef);
926 this.useCachedFactory = true;
927 if (TD.ClientBaseCachedChannelFactoryCountIsEnabled())
929 TD.ClientBaseCachedChannelFactoryCount(factoryRefCache.Count, maxNumChannelFactories, this);
935 // NOTE: This should be called inside ThisLock
936 void InvalidateCacheAndCreateChannel()
938 RemoveFactoryFromCache();
940 CreateChannelInternal();
943 void RemoveFactoryFromCache()
947 ChannelFactoryRef<TChannel> factoryRef;
948 if (factoryRefCache.TryGetValue(this.endpointTrait, out factoryRef))
950 if (object.ReferenceEquals(this.channelFactoryRef, factoryRef))
952 factoryRefCache.Remove(this.endpointTrait);
958 // WARNING: changes in the signature/name of the following delegates must be applied to the
959 // ClientClassGenerator.cs as well, otherwise the ClientClassGenerator would generate wrong code.
960 protected delegate IAsyncResult BeginOperationDelegate(object[] inValues, AsyncCallback asyncCallback, object state);
961 protected delegate object[] EndOperationDelegate(IAsyncResult result);
963 // WARNING: Any changes in the signature/name of the following type and its ctor must be applied to the
964 // ClientClassGenerator.cs as well, otherwise the ClientClassGenerator would generate wrong code.
965 protected class InvokeAsyncCompletedEventArgs : AsyncCompletedEventArgs
969 internal InvokeAsyncCompletedEventArgs(object[] results, Exception error, bool cancelled, object userState)
970 : base(error, cancelled, userState)
972 this.results = results;
975 public object[] Results
984 // WARNING: Any changes in the signature/name of the following method ctor must be applied to the
985 // ClientClassGenerator.cs as well, otherwise the ClientClassGenerator would generate wrong code.
986 protected void InvokeAsync(BeginOperationDelegate beginOperationDelegate, object[] inValues,
987 EndOperationDelegate endOperationDelegate, SendOrPostCallback operationCompletedCallback, object userState)
989 if (beginOperationDelegate == null)
991 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("beginOperationDelegate");
993 if (endOperationDelegate == null)
995 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endOperationDelegate");
998 AsyncOperation asyncOperation = AsyncOperationManager.CreateOperation(userState);
999 AsyncOperationContext context = new AsyncOperationContext(asyncOperation, endOperationDelegate, operationCompletedCallback);
1001 Exception error = null;
1002 object[] results = null;
1003 IAsyncResult result = null;
1006 result = beginOperationDelegate(inValues, onAsyncCallCompleted, context);
1007 if (result.CompletedSynchronously)
1009 results = endOperationDelegate(result);
1021 if (error != null || result.CompletedSynchronously) /* result cannot be null if error == null */
1023 CompleteAsyncCall(context, results, error);
1027 static void OnAsyncCallCompleted(IAsyncResult result)
1029 if (result.CompletedSynchronously)
1034 AsyncOperationContext context = (AsyncOperationContext)result.AsyncState;
1035 Exception error = null;
1036 object[] results = null;
1039 results = context.EndDelegate(result);
1051 CompleteAsyncCall(context, results, error);
1054 static void CompleteAsyncCall(AsyncOperationContext context, object[] results, Exception error)
1056 if (context.CompletionCallback != null)
1058 InvokeAsyncCompletedEventArgs e = new InvokeAsyncCompletedEventArgs(results, error, false, context.AsyncOperation.UserSuppliedState);
1059 context.AsyncOperation.PostOperationCompleted(context.CompletionCallback, e);
1063 context.AsyncOperation.OperationCompleted();
1067 class AsyncOperationContext
1069 AsyncOperation asyncOperation;
1070 EndOperationDelegate endDelegate;
1071 SendOrPostCallback completionCallback;
1073 internal AsyncOperationContext(AsyncOperation asyncOperation, EndOperationDelegate endDelegate, SendOrPostCallback completionCallback)
1075 this.asyncOperation = asyncOperation;
1076 this.endDelegate = endDelegate;
1077 this.completionCallback = completionCallback;
1080 internal AsyncOperation AsyncOperation
1084 return this.asyncOperation;
1088 internal EndOperationDelegate EndDelegate
1092 return this.endDelegate;
1096 internal SendOrPostCallback CompletionCallback
1100 return this.completionCallback;
1105 protected class ChannelBase<T> : IClientChannel, IOutputChannel, IRequestChannel, IChannelBaseProxy
1108 ServiceChannel channel;
1109 System.ServiceModel.Dispatcher.ImmutableClientRuntime runtime;
1111 protected ChannelBase(ClientBase<T> client)
1113 if (client.Endpoint.Address == null)
1115 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxChannelFactoryEndpointAddressUri)));
1118 ChannelFactory<T> cf = client.ChannelFactory;
1119 cf.EnsureOpened(); // to prevent the NullReferenceException that is thrown if the ChannelFactory is not open when cf.ServiceChannelFactory is accessed.
1120 this.channel = cf.ServiceChannelFactory.CreateServiceChannel(client.Endpoint.Address, client.Endpoint.Address.Uri);
1121 this.channel.InstanceContext = cf.CallbackInstance;
1122 this.runtime = this.channel.ClientRuntime.GetRuntime();
1125 [Fx.Tag.SecurityNote(Critical = "Accesses the critical IMethodCallMessage interface.",
1126 Safe = "The implementation of IMethodCallMessage is local and is created locally as weell; i.e. not passed in from Remoting.")]
1127 [SecuritySafeCritical]
1128 protected IAsyncResult BeginInvoke(string methodName, object[] args, AsyncCallback callback, object state)
1130 object[] inArgs = new object[args.Length + 2];
1131 Array.Copy(args, inArgs, args.Length);
1132 inArgs[inArgs.Length - 2] = callback;
1133 inArgs[inArgs.Length - 1] = state;
1135 IMethodCallMessage methodCall = new MethodCallMessage(inArgs);
1136 ProxyOperationRuntime op = GetOperationByName(methodName);
1137 object[] ins = op.MapAsyncBeginInputs(methodCall, out callback, out state);
1138 return this.channel.BeginCall(op.Action, op.IsOneWay, op, ins, callback, state);
1141 [Fx.Tag.SecurityNote(Critical = "Accesses the critical IMethodCallMessage interface.",
1142 Safe = "The implementation of IMethodCallMessage is local and is created locally as weell; i.e. not passed in from Remoting.")]
1143 [SecuritySafeCritical]
1144 protected object EndInvoke(string methodName, object[] args, IAsyncResult result)
1146 object[] inArgs = new object[args.Length + 1];
1147 Array.Copy(args, inArgs, args.Length);
1148 inArgs[inArgs.Length - 1] = result;
1150 IMethodCallMessage methodCall = new MethodCallMessage(inArgs);
1151 ProxyOperationRuntime op = GetOperationByName(methodName);
1153 op.MapAsyncEndInputs(methodCall, out result, out outs);
1154 object ret = this.channel.EndCall(op.Action, outs, result);
1155 object[] retArgs = op.MapAsyncOutputs(methodCall, outs, ref ret);
1156 if (retArgs != null)
1158 Fx.Assert(retArgs.Length == inArgs.Length, "retArgs.Length should be equal to inArgs.Length");
1159 Array.Copy(retArgs, args, args.Length);
1164 System.ServiceModel.Dispatcher.ProxyOperationRuntime GetOperationByName(string methodName)
1166 ProxyOperationRuntime op = this.runtime.GetOperationByName(methodName);
1169 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SFxMethodNotSupported1, methodName)));
1174 bool IClientChannel.AllowInitializationUI
1176 get { return ((IClientChannel)this.channel).AllowInitializationUI; }
1177 set { ((IClientChannel)this.channel).AllowInitializationUI = value; }
1180 bool IClientChannel.DidInteractiveInitialization
1182 get { return ((IClientChannel)this.channel).DidInteractiveInitialization; }
1185 Uri IClientChannel.Via
1187 get { return ((IClientChannel)this.channel).Via; }
1190 event EventHandler<UnknownMessageReceivedEventArgs> IClientChannel.UnknownMessageReceived
1192 add { ((IClientChannel)this.channel).UnknownMessageReceived += value; }
1193 remove { ((IClientChannel)this.channel).UnknownMessageReceived -= value; }
1196 void IClientChannel.DisplayInitializationUI()
1198 ((IClientChannel)this.channel).DisplayInitializationUI();
1201 IAsyncResult IClientChannel.BeginDisplayInitializationUI(AsyncCallback callback, object state)
1203 return ((IClientChannel)this.channel).BeginDisplayInitializationUI(callback, state);
1206 void IClientChannel.EndDisplayInitializationUI(IAsyncResult result)
1208 ((IClientChannel)this.channel).EndDisplayInitializationUI(result);
1211 bool IContextChannel.AllowOutputBatching
1213 get { return ((IContextChannel)this.channel).AllowOutputBatching; }
1214 set { ((IContextChannel)this.channel).AllowOutputBatching = value; }
1217 IInputSession IContextChannel.InputSession
1219 get { return ((IContextChannel)this.channel).InputSession; }
1222 EndpointAddress IContextChannel.LocalAddress
1224 get { return ((IContextChannel)this.channel).LocalAddress; }
1227 TimeSpan IContextChannel.OperationTimeout
1229 get { return ((IContextChannel)this.channel).OperationTimeout; }
1230 set { ((IContextChannel)this.channel).OperationTimeout = value; }
1233 IOutputSession IContextChannel.OutputSession
1235 get { return ((IContextChannel)this.channel).OutputSession; }
1238 EndpointAddress IContextChannel.RemoteAddress
1240 get { return ((IContextChannel)this.channel).RemoteAddress; }
1243 string IContextChannel.SessionId
1245 get { return ((IContextChannel)this.channel).SessionId; }
1248 TProperty IChannel.GetProperty<TProperty>()
1250 return ((IChannel)this.channel).GetProperty<TProperty>();
1253 CommunicationState ICommunicationObject.State
1255 get { return ((ICommunicationObject)this.channel).State; }
1258 event EventHandler ICommunicationObject.Closed
1260 add { ((ICommunicationObject)this.channel).Closed += value; }
1261 remove { ((ICommunicationObject)this.channel).Closed -= value; }
1264 event EventHandler ICommunicationObject.Closing
1266 add { ((ICommunicationObject)this.channel).Closing += value; }
1267 remove { ((ICommunicationObject)this.channel).Closing -= value; }
1270 event EventHandler ICommunicationObject.Faulted
1272 add { ((ICommunicationObject)this.channel).Faulted += value; }
1273 remove { ((ICommunicationObject)this.channel).Faulted -= value; }
1276 event EventHandler ICommunicationObject.Opened
1278 add { ((ICommunicationObject)this.channel).Opened += value; }
1279 remove { ((ICommunicationObject)this.channel).Opened -= value; }
1282 event EventHandler ICommunicationObject.Opening
1284 add { ((ICommunicationObject)this.channel).Opening += value; }
1285 remove { ((ICommunicationObject)this.channel).Opening -= value; }
1288 void ICommunicationObject.Abort()
1290 ((ICommunicationObject)this.channel).Abort();
1293 void ICommunicationObject.Close()
1295 ((ICommunicationObject)this.channel).Close();
1298 void ICommunicationObject.Close(TimeSpan timeout)
1300 ((ICommunicationObject)this.channel).Close(timeout);
1303 IAsyncResult ICommunicationObject.BeginClose(AsyncCallback callback, object state)
1305 return ((ICommunicationObject)this.channel).BeginClose(callback, state);
1308 IAsyncResult ICommunicationObject.BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
1310 return ((ICommunicationObject)this.channel).BeginClose(timeout, callback, state);
1313 void ICommunicationObject.EndClose(IAsyncResult result)
1315 ((ICommunicationObject)this.channel).EndClose(result);
1318 void ICommunicationObject.Open()
1320 ((ICommunicationObject)this.channel).Open();
1323 void ICommunicationObject.Open(TimeSpan timeout)
1325 ((ICommunicationObject)this.channel).Open(timeout);
1328 IAsyncResult ICommunicationObject.BeginOpen(AsyncCallback callback, object state)
1330 return ((ICommunicationObject)this.channel).BeginOpen(callback, state);
1333 IAsyncResult ICommunicationObject.BeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
1335 return ((ICommunicationObject)this.channel).BeginOpen(timeout, callback, state);
1338 void ICommunicationObject.EndOpen(IAsyncResult result)
1340 ((ICommunicationObject)this.channel).EndOpen(result);
1343 IExtensionCollection<IContextChannel> IExtensibleObject<IContextChannel>.Extensions
1345 get { return ((IExtensibleObject<IContextChannel>)this.channel).Extensions; }
1348 void IDisposable.Dispose()
1350 ((IDisposable)this.channel).Dispose();
1353 Uri IOutputChannel.Via
1355 get { return ((IOutputChannel)this.channel).Via; }
1358 EndpointAddress IOutputChannel.RemoteAddress
1360 get { return ((IOutputChannel)this.channel).RemoteAddress; }
1363 void IOutputChannel.Send(Message message)
1365 ((IOutputChannel)this.channel).Send(message);
1368 void IOutputChannel.Send(Message message, TimeSpan timeout)
1370 ((IOutputChannel)this.channel).Send(message, timeout);
1373 IAsyncResult IOutputChannel.BeginSend(Message message, AsyncCallback callback, object state)
1375 return ((IOutputChannel)this.channel).BeginSend(message, callback, state);
1378 IAsyncResult IOutputChannel.BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state)
1380 return ((IOutputChannel)this.channel).BeginSend(message, timeout, callback, state);
1383 void IOutputChannel.EndSend(IAsyncResult result)
1385 ((IOutputChannel)this.channel).EndSend(result);
1388 Uri IRequestChannel.Via
1390 get { return ((IRequestChannel)this.channel).Via; }
1393 EndpointAddress IRequestChannel.RemoteAddress
1395 get { return ((IRequestChannel)this.channel).RemoteAddress; }
1398 Message IRequestChannel.Request(Message message)
1400 return ((IRequestChannel)this.channel).Request(message);
1403 Message IRequestChannel.Request(Message message, TimeSpan timeout)
1405 return ((IRequestChannel)this.channel).Request(message, timeout);
1408 IAsyncResult IRequestChannel.BeginRequest(Message message, AsyncCallback callback, object state)
1410 return ((IRequestChannel)this.channel).BeginRequest(message, callback, state);
1413 IAsyncResult IRequestChannel.BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state)
1415 return ((IRequestChannel)this.channel).BeginRequest(message, timeout, callback, state);
1418 Message IRequestChannel.EndRequest(IAsyncResult result)
1420 return ((IRequestChannel)this.channel).EndRequest(result);
1423 ServiceChannel IChannelBaseProxy.GetServiceChannel()
1425 return this.channel;
1428 class MethodCallMessage : IMethodCallMessage
1430 readonly object[] args;
1432 public MethodCallMessage(object[] args)
1437 public object[] Args
1439 get { return this.args; }
1444 get { return this.args.Length; }
1447 public LogicalCallContext LogicalCallContext
1449 get { return null; }
1452 public object GetInArg(int argNum)
1454 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
1457 public string GetInArgName(int index)
1459 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
1462 public int InArgCount
1466 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
1470 public object[] InArgs
1472 get { return this.args; }
1476 public object GetArg(int argNum)
1478 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
1481 public string GetArgName(int index)
1483 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
1486 public bool HasVarArgs
1490 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
1494 public Reflection.MethodBase MethodBase
1498 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
1502 public string MethodName
1506 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
1510 public object MethodSignature
1514 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
1518 public string TypeName
1522 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
1530 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
1534 public Collections.IDictionary Properties
1538 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());