|
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
namespace System.ServiceModel
{
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Diagnostics;
using System.Threading;
using System.ServiceModel.Diagnostics.Application;
using System.Runtime.Remoting.Messaging;
using System.ServiceModel.Dispatcher;
using System.Security;
public abstract class ClientBase<TChannel> : ICommunicationObject, IDisposable
where TChannel : class
{
TChannel channel;
ChannelFactoryRef<TChannel> channelFactoryRef;
EndpointTrait<TChannel> endpointTrait;
// Determine whether the proxy can share factory with others. It is false only if the public getters
// are invoked.
bool canShareFactory = true;
// Determine whether the proxy is currently holding a cached factory
bool useCachedFactory;
// Determine whether we have locked down sharing for this proxy. This is turned on only when the channel
// is created.
bool sharingFinalized;
// Determine whether the ChannelFactoryRef has been released. We should release it only once per proxy
bool channelFactoryRefReleased;
// Determine whether we have released the last ref count of the ChannelFactory so that we could abort it when it was closing.
bool releasedLastRef;
object syncRoot = new object();
object finalizeLock = new object();
// Cache at most 32 ChannelFactories
const int maxNumChannelFactories = 32;
static ChannelFactoryRefCache<TChannel> factoryRefCache = new ChannelFactoryRefCache<TChannel>(maxNumChannelFactories);
static object staticLock = new object();
static object cacheLock = new object();
static CacheSetting cacheSetting = CacheSetting.Default;
static bool isCacheSettingReadOnly;
static AsyncCallback onAsyncCallCompleted = Fx.ThunkCallback(new AsyncCallback(OnAsyncCallCompleted));
// IMPORTANT: any changes to the set of protected .ctors of this class need to be reflected
// in ServiceContractGenerator.cs as well.
protected ClientBase()
{
MakeCacheSettingReadOnly();
if (cacheSetting == CacheSetting.AlwaysOff)
{
this.channelFactoryRef = new ChannelFactoryRef<TChannel>(new ChannelFactory<TChannel>("*"));
this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
TryDisableSharing();
}
else
{
this.endpointTrait = new ConfigurationEndpointTrait<TChannel>("*", null, null);
InitializeChannelFactoryRef();
}
}
protected ClientBase(string endpointConfigurationName)
{
if (endpointConfigurationName == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointConfigurationName");
MakeCacheSettingReadOnly();
if (cacheSetting == CacheSetting.AlwaysOff)
{
this.channelFactoryRef = new ChannelFactoryRef<TChannel>(new ChannelFactory<TChannel>(endpointConfigurationName));
this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
TryDisableSharing();
}
else
{
this.endpointTrait = new ConfigurationEndpointTrait<TChannel>(endpointConfigurationName, null, null);
InitializeChannelFactoryRef();
}
}
protected ClientBase(string endpointConfigurationName, string remoteAddress)
{
if (endpointConfigurationName == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointConfigurationName");
if (remoteAddress == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("remoteAddress");
MakeCacheSettingReadOnly();
EndpointAddress endpointAddress = new EndpointAddress(remoteAddress);
if (cacheSetting == CacheSetting.AlwaysOff)
{
this.channelFactoryRef = new ChannelFactoryRef<TChannel>(new ChannelFactory<TChannel>(endpointConfigurationName, endpointAddress));
this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
TryDisableSharing();
}
else
{
this.endpointTrait = new ConfigurationEndpointTrait<TChannel>(endpointConfigurationName, endpointAddress, null);
InitializeChannelFactoryRef();
}
}
protected ClientBase(string endpointConfigurationName, EndpointAddress remoteAddress)
{
if (endpointConfigurationName == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointConfigurationName");
if (remoteAddress == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("remoteAddress");
MakeCacheSettingReadOnly();
if (cacheSetting == CacheSetting.AlwaysOff)
{
this.channelFactoryRef = new ChannelFactoryRef<TChannel>(new ChannelFactory<TChannel>(endpointConfigurationName, remoteAddress));
this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
TryDisableSharing();
}
else
{
this.endpointTrait = new ConfigurationEndpointTrait<TChannel>(endpointConfigurationName, remoteAddress, null);
InitializeChannelFactoryRef();
}
}
protected ClientBase(Binding binding, EndpointAddress remoteAddress)
{
if (binding == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("binding");
if (remoteAddress == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("remoteAddress");
MakeCacheSettingReadOnly();
if (cacheSetting == CacheSetting.AlwaysOn)
{
this.endpointTrait = new ProgrammaticEndpointTrait<TChannel>(binding, remoteAddress, null);
InitializeChannelFactoryRef();
}
else
{
this.channelFactoryRef = new ChannelFactoryRef<TChannel>(new ChannelFactory<TChannel>(binding, remoteAddress));
this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
TryDisableSharing();
}
}
protected ClientBase(ServiceEndpoint endpoint)
{
if (endpoint == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpoint");
MakeCacheSettingReadOnly();
if (cacheSetting == CacheSetting.AlwaysOn)
{
this.endpointTrait = new ServiceEndpointTrait<TChannel>(endpoint, null);
this.InitializeChannelFactoryRef();
}
else
{
channelFactoryRef = new ChannelFactoryRef<TChannel>(new ChannelFactory<TChannel>(endpoint));
channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
TryDisableSharing();
}
}
protected ClientBase(InstanceContext callbackInstance)
{
if (callbackInstance == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("callbackInstance");
MakeCacheSettingReadOnly();
if (cacheSetting == CacheSetting.AlwaysOff)
{
this.channelFactoryRef = new ChannelFactoryRef<TChannel>(
new DuplexChannelFactory<TChannel>(callbackInstance, "*"));
this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
TryDisableSharing();
}
else
{
this.endpointTrait = new ConfigurationEndpointTrait<TChannel>("*", null, callbackInstance);
InitializeChannelFactoryRef();
}
}
protected ClientBase(InstanceContext callbackInstance, string endpointConfigurationName)
{
if (callbackInstance == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("callbackInstance");
if (endpointConfigurationName == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointConfigurationName");
MakeCacheSettingReadOnly();
if (cacheSetting == CacheSetting.AlwaysOff)
{
this.channelFactoryRef = new ChannelFactoryRef<TChannel>(
new DuplexChannelFactory<TChannel>(callbackInstance, endpointConfigurationName));
this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
TryDisableSharing();
}
else
{
this.endpointTrait = new ConfigurationEndpointTrait<TChannel>(endpointConfigurationName, null, callbackInstance);
InitializeChannelFactoryRef();
}
}
protected ClientBase(InstanceContext callbackInstance, string endpointConfigurationName, string remoteAddress)
{
if (callbackInstance == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("callbackInstance");
if (endpointConfigurationName == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointConfigurationName");
if (remoteAddress == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("remoteAddress");
MakeCacheSettingReadOnly();
EndpointAddress endpointAddress = new EndpointAddress(remoteAddress);
if (cacheSetting == CacheSetting.AlwaysOff)
{
this.channelFactoryRef = new ChannelFactoryRef<TChannel>(
new DuplexChannelFactory<TChannel>(callbackInstance, endpointConfigurationName, endpointAddress));
this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
TryDisableSharing();
}
else
{
this.endpointTrait = new ConfigurationEndpointTrait<TChannel>(endpointConfigurationName, endpointAddress, callbackInstance);
InitializeChannelFactoryRef();
}
}
protected ClientBase(InstanceContext callbackInstance, string endpointConfigurationName, EndpointAddress remoteAddress)
{
if (callbackInstance == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("callbackInstance");
if (endpointConfigurationName == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointConfigurationName");
if (remoteAddress == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("remoteAddress");
MakeCacheSettingReadOnly();
if (cacheSetting == CacheSetting.AlwaysOff)
{
this.channelFactoryRef = new ChannelFactoryRef<TChannel>(
new DuplexChannelFactory<TChannel>(callbackInstance, endpointConfigurationName, remoteAddress));
this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
TryDisableSharing();
}
else
{
this.endpointTrait = new ConfigurationEndpointTrait<TChannel>(endpointConfigurationName, remoteAddress, callbackInstance);
InitializeChannelFactoryRef();
}
}
protected ClientBase(InstanceContext callbackInstance, Binding binding, EndpointAddress remoteAddress)
{
if (callbackInstance == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("callbackInstance");
if (binding == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("binding");
if (remoteAddress == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("remoteAddress");
MakeCacheSettingReadOnly();
if (cacheSetting == CacheSetting.AlwaysOn)
{
this.endpointTrait = new ProgrammaticEndpointTrait<TChannel>(binding, remoteAddress, callbackInstance);
InitializeChannelFactoryRef();
}
else
{
this.channelFactoryRef = new ChannelFactoryRef<TChannel>(
new DuplexChannelFactory<TChannel>(callbackInstance, binding, remoteAddress));
this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
TryDisableSharing();
}
}
protected ClientBase(InstanceContext callbackInstance, ServiceEndpoint endpoint)
{
if (callbackInstance == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("callbackInstance");
if (endpoint == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpoint");
MakeCacheSettingReadOnly();
if (cacheSetting == CacheSetting.AlwaysOn)
{
this.endpointTrait = new ServiceEndpointTrait<TChannel>(endpoint, callbackInstance);
InitializeChannelFactoryRef();
}
else
{
this.channelFactoryRef = new ChannelFactoryRef<TChannel>(
new DuplexChannelFactory<TChannel>(callbackInstance, endpoint));
this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false;
TryDisableSharing();
}
}
protected T GetDefaultValueForInitialization<T>()
{
return default(T);
}
object ThisLock
{
get
{
return syncRoot;
}
}
protected TChannel Channel
{
get
{
// created on demand, so that Mort can modify .Endpoint before calling methods on the client
if (this.channel == null)
{
lock (ThisLock)
{
if (this.channel == null)
{
using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.CreateBoundedActivity() : null)
{
if (DiagnosticUtility.ShouldUseActivity)
{
ServiceModelActivity.Start(activity, SR.GetString(SR.ActivityOpenClientBase, typeof(TChannel).FullName), ActivityType.OpenClient);
}
if (this.useCachedFactory)
{
try
{
CreateChannelInternal();
}
#pragma warning suppress 56500 // covered by FxCOP
catch (Exception ex)
{
if (this.useCachedFactory &&
(ex is CommunicationException ||
ex is ObjectDisposedException ||
ex is TimeoutException))
{
DiagnosticUtility.TraceHandledException(ex, TraceEventType.Warning);
InvalidateCacheAndCreateChannel();
}
else
{
#pragma warning suppress 56503 // Microsoft, We throw only for unknown exceptions.
throw;
}
}
}
else
{
CreateChannelInternal();
}
}
}
}
}
return channel;
}
}
public static CacheSetting CacheSetting
{
get
{
return cacheSetting;
}
set
{
lock (cacheLock)
{
if (isCacheSettingReadOnly && cacheSetting != value)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxImmutableClientBaseCacheSetting, typeof(TChannel).ToString())));
}
else
{
cacheSetting = value;
}
}
}
}
public ChannelFactory<TChannel> ChannelFactory
{
get
{
if (cacheSetting == CacheSetting.Default)
{
TryDisableSharing();
}
return GetChannelFactory();
}
}
public ClientCredentials ClientCredentials
{
get
{
if (cacheSetting == CacheSetting.Default)
{
TryDisableSharing();
}
return this.ChannelFactory.Credentials;
}
}
public CommunicationState State
{
get
{
IChannel channel = (IChannel)this.channel;
if (channel != null)
{
return channel.State;
}
else
{
// we may have failed to create the channel under open, in which case we our factory wouldn't be open
if (!this.useCachedFactory)
{
return GetChannelFactory().State;
}
else
{
return CommunicationState.Created;
}
}
}
}
public IClientChannel InnerChannel
{
get
{
return (IClientChannel)Channel;
}
}
public ServiceEndpoint Endpoint
{
get
{
if (cacheSetting == CacheSetting.Default)
{
TryDisableSharing();
}
return GetChannelFactory().Endpoint;
}
}
public void Open()
{
((ICommunicationObject)this).Open(GetChannelFactory().InternalOpenTimeout);
}
public void Abort()
{
IChannel channel = (IChannel)this.channel;
if (channel != null)
{
channel.Abort();
}
if (!channelFactoryRefReleased)
{
lock (staticLock)
{
if (!channelFactoryRefReleased)
{
if (this.channelFactoryRef.Release())
{
this.releasedLastRef = true;
}
channelFactoryRefReleased = true;
}
}
}
// Abort the ChannelFactory if we released the last one. We should be able to abort it when another thread is closing it.
if (this.releasedLastRef)
{
this.channelFactoryRef.Abort();
}
}
public void Close()
{
((ICommunicationObject)this).Close(GetChannelFactory().InternalCloseTimeout);
}
public void DisplayInitializationUI()
{
((IClientChannel)this.InnerChannel).DisplayInitializationUI();
}
// This ensures that the cachesetting (on, off or default) cannot be modified by
// another ClientBase instance of matching TChannel after the first instance is created.
void MakeCacheSettingReadOnly()
{
if (isCacheSettingReadOnly)
return;
lock (cacheLock)
{
isCacheSettingReadOnly = true;
}
}
void CreateChannelInternal()
{
try
{
this.channel = this.CreateChannel();
if (this.sharingFinalized)
{
if (this.canShareFactory && !this.useCachedFactory)
{
// It is OK to add ChannelFactory to the cache now.
TryAddChannelFactoryToCache();
}
}
}
finally
{
if (!this.sharingFinalized && cacheSetting == CacheSetting.Default)
{
// this.CreateChannel() is not called. For safety, we disable sharing.
TryDisableSharing();
}
}
}
protected virtual TChannel CreateChannel()
{
if (this.sharingFinalized)
return GetChannelFactory().CreateChannel();
lock (this.finalizeLock)
{
this.sharingFinalized = true;
return GetChannelFactory().CreateChannel();
}
}
void IDisposable.Dispose()
{
this.Close();
}
void ICommunicationObject.Open(TimeSpan timeout)
{
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
if (!this.useCachedFactory)
{
GetChannelFactory().Open(timeoutHelper.RemainingTime());
}
this.InnerChannel.Open(timeoutHelper.RemainingTime());
}
void ICommunicationObject.Close(TimeSpan timeout)
{
using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.CreateBoundedActivity() : null)
{
if (DiagnosticUtility.ShouldUseActivity)
{
ServiceModelActivity.Start(activity, SR.GetString(SR.ActivityCloseClientBase, typeof(TChannel).FullName), ActivityType.Close);
}
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
if (this.channel != null)
{
InnerChannel.Close(timeoutHelper.RemainingTime());
}
if (!channelFactoryRefReleased)
{
lock (staticLock)
{
if (!channelFactoryRefReleased)
{
if (this.channelFactoryRef.Release())
{
this.releasedLastRef = true;
}
this.channelFactoryRefReleased = true;
}
}
// Close the factory outside of the lock so that we can abort from a different thread.
if (this.releasedLastRef)
{
if (this.useCachedFactory)
{
this.channelFactoryRef.Abort();
}
else
{
this.channelFactoryRef.Close(timeoutHelper.RemainingTime());
}
}
}
}
}
event EventHandler ICommunicationObject.Closed
{
add
{
this.InnerChannel.Closed += value;
}
remove
{
this.InnerChannel.Closed -= value;
}
}
event EventHandler ICommunicationObject.Closing
{
add
{
this.InnerChannel.Closing += value;
}
remove
{
this.InnerChannel.Closing -= value;
}
}
event EventHandler ICommunicationObject.Faulted
{
add
{
this.InnerChannel.Faulted += value;
}
remove
{
this.InnerChannel.Faulted -= value;
}
}
event EventHandler ICommunicationObject.Opened
{
add
{
this.InnerChannel.Opened += value;
}
remove
{
this.InnerChannel.Opened -= value;
}
}
event EventHandler ICommunicationObject.Opening
{
add
{
this.InnerChannel.Opening += value;
}
remove
{
this.InnerChannel.Opening -= value;
}
}
IAsyncResult ICommunicationObject.BeginClose(AsyncCallback callback, object state)
{
return ((ICommunicationObject)this).BeginClose(GetChannelFactory().InternalCloseTimeout, callback, state);
}
IAsyncResult ICommunicationObject.BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
{
return new ChainedAsyncResult(timeout, callback, state, BeginChannelClose, EndChannelClose, BeginFactoryClose, EndFactoryClose);
}
void ICommunicationObject.EndClose(IAsyncResult result)
{
ChainedAsyncResult.End(result);
}
IAsyncResult ICommunicationObject.BeginOpen(AsyncCallback callback, object state)
{
return ((ICommunicationObject)this).BeginOpen(GetChannelFactory().InternalOpenTimeout, callback, state);
}
IAsyncResult ICommunicationObject.BeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
{
return new ChainedAsyncResult(timeout, callback, state, BeginFactoryOpen, EndFactoryOpen, BeginChannelOpen, EndChannelOpen);
}
void ICommunicationObject.EndOpen(IAsyncResult result)
{
ChainedAsyncResult.End(result);
}
//ChainedAsyncResult methods for opening and closing ChannelFactory<T>
internal IAsyncResult BeginFactoryOpen(TimeSpan timeout, AsyncCallback callback, object state)
{
if (this.useCachedFactory)
{
return new CompletedAsyncResult(callback, state);
}
else
{
return GetChannelFactory().BeginOpen(timeout, callback, state);
}
}
internal void EndFactoryOpen(IAsyncResult result)
{
if (this.useCachedFactory)
{
CompletedAsyncResult.End(result);
}
else
{
GetChannelFactory().EndOpen(result);
}
}
internal IAsyncResult BeginChannelOpen(TimeSpan timeout, AsyncCallback callback, object state)
{
return this.InnerChannel.BeginOpen(timeout, callback, state);
}
internal void EndChannelOpen(IAsyncResult result)
{
this.InnerChannel.EndOpen(result);
}
internal IAsyncResult BeginFactoryClose(TimeSpan timeout, AsyncCallback callback, object state)
{
if (this.useCachedFactory)
{
return new CompletedAsyncResult(callback, state);
}
else
{
return GetChannelFactory().BeginClose(timeout, callback, state);
}
}
internal void EndFactoryClose(IAsyncResult result)
{
if (typeof(CompletedAsyncResult).IsAssignableFrom(result.GetType()))
{
CompletedAsyncResult.End(result);
}
else
{
GetChannelFactory().EndClose(result);
}
}
internal IAsyncResult BeginChannelClose(TimeSpan timeout, AsyncCallback callback, object state)
{
if (this.channel != null)
{
return this.InnerChannel.BeginClose(timeout, callback, state);
}
else
{
return new CompletedAsyncResult(callback, state);
}
}
internal void EndChannelClose(IAsyncResult result)
{
if (typeof(CompletedAsyncResult).IsAssignableFrom(result.GetType()))
{
CompletedAsyncResult.End(result);
}
else
{
this.InnerChannel.EndClose(result);
}
}
ChannelFactory<TChannel> GetChannelFactory()
{
return this.channelFactoryRef.ChannelFactory;
}
void InitializeChannelFactoryRef()
{
Fx.Assert(this.channelFactoryRef == null, "The channelFactory should have never been assigned");
Fx.Assert(this.canShareFactory, "GetChannelFactoryFromCache can be called only when canShareFactory is true");
lock (staticLock)
{
ChannelFactoryRef<TChannel> factoryRef;
if (factoryRefCache.TryGetValue(this.endpointTrait, out factoryRef))
{
if (factoryRef.ChannelFactory.State != CommunicationState.Opened)
{
// Remove the bad ChannelFactory.
factoryRefCache.Remove(this.endpointTrait);
}
else
{
this.channelFactoryRef = factoryRef;
this.channelFactoryRef.AddRef();
useCachedFactory = true;
if (TD.ClientBaseChannelFactoryCacheHitIsEnabled())
{
TD.ClientBaseChannelFactoryCacheHit(this);
}
return;
}
}
}
if (this.channelFactoryRef == null)
{
// Creating the ChannelFactory at initial time to catch configuration exception earlier.
this.channelFactoryRef = CreateChannelFactoryRef(this.endpointTrait);
}
}
static ChannelFactoryRef<TChannel> CreateChannelFactoryRef(EndpointTrait<TChannel> endpointTrait)
{
Fx.Assert(endpointTrait != null, "The endpointTrait should not be null when the factory can be shared.");
ChannelFactory<TChannel> channelFactory = endpointTrait.CreateChannelFactory();
channelFactory.TraceOpenAndClose = false;
return new ChannelFactoryRef<TChannel>(channelFactory);
}
// Once the channel is created, we can't disable caching.
// This method can be called safely multiple times.
// this.sharingFinalized is set the first time the method is called.
// Subsequent calls are essentially no-ops.
void TryDisableSharing()
{
if (this.sharingFinalized)
return;
lock (this.finalizeLock)
{
if (this.sharingFinalized)
return;
this.canShareFactory = false;
this.sharingFinalized = true;
if (this.useCachedFactory)
{
ChannelFactoryRef<TChannel> pendingFactoryRef = this.channelFactoryRef;
this.channelFactoryRef = CreateChannelFactoryRef(this.endpointTrait);
this.useCachedFactory = false;
lock (staticLock)
{
if (!pendingFactoryRef.Release())
{
pendingFactoryRef = null;
}
}
if (pendingFactoryRef != null)
pendingFactoryRef.Abort();
}
}
// can be done outside the lock since the lines below do not access shared data.
// also the use of this.sharingFinalized in the lines above ensures that tracing
// happens only once and only when needed.
if (TD.ClientBaseUsingLocalChannelFactoryIsEnabled())
{
TD.ClientBaseUsingLocalChannelFactory(this);
}
}
void TryAddChannelFactoryToCache()
{
Fx.Assert(this.canShareFactory, "This should be called only when this proxy can share ChannelFactory.");
Fx.Assert(this.channelFactoryRef.ChannelFactory.State == CommunicationState.Opened,
"The ChannelFactory must be in Opened state for caching.");
// Lock the cache and add the item to synchronize with lookup.
lock (staticLock)
{
ChannelFactoryRef<TChannel> cfRef;
if (!factoryRefCache.TryGetValue(this.endpointTrait, out cfRef))
{
// Increment the ref count before adding to the cache.
this.channelFactoryRef.AddRef();
factoryRefCache.Add(this.endpointTrait, this.channelFactoryRef);
this.useCachedFactory = true;
if (TD.ClientBaseCachedChannelFactoryCountIsEnabled())
{
TD.ClientBaseCachedChannelFactoryCount(factoryRefCache.Count, maxNumChannelFactories, this);
}
}
}
}
// NOTE: This should be called inside ThisLock
void InvalidateCacheAndCreateChannel()
{
RemoveFactoryFromCache();
TryDisableSharing();
CreateChannelInternal();
}
void RemoveFactoryFromCache()
{
lock (staticLock)
{
ChannelFactoryRef<TChannel> factoryRef;
if (factoryRefCache.TryGetValue(this.endpointTrait, out factoryRef))
{
if (object.ReferenceEquals(this.channelFactoryRef, factoryRef))
{
factoryRefCache.Remove(this.endpointTrait);
}
}
}
}
// WARNING: changes in the signature/name of the following delegates must be applied to the
// ClientClassGenerator.cs as well, otherwise the ClientClassGenerator would generate wrong code.
protected delegate IAsyncResult BeginOperationDelegate(object[] inValues, AsyncCallback asyncCallback, object state);
protected delegate object[] EndOperationDelegate(IAsyncResult result);
// WARNING: Any changes in the signature/name of the following type and its ctor must be applied to the
// ClientClassGenerator.cs as well, otherwise the ClientClassGenerator would generate wrong code.
protected class InvokeAsyncCompletedEventArgs : AsyncCompletedEventArgs
{
object[] results;
internal InvokeAsyncCompletedEventArgs(object[] results, Exception error, bool cancelled, object userState)
: base(error, cancelled, userState)
{
this.results = results;
}
public object[] Results
{
get
{
return this.results;
}
}
}
// WARNING: Any changes in the signature/name of the following method ctor must be applied to the
// ClientClassGenerator.cs as well, otherwise the ClientClassGenerator would generate wrong code.
protected void InvokeAsync(BeginOperationDelegate beginOperationDelegate, object[] inValues,
EndOperationDelegate endOperationDelegate, SendOrPostCallback operationCompletedCallback, object userState)
{
if (beginOperationDelegate == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("beginOperationDelegate");
}
if (endOperationDelegate == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endOperationDelegate");
}
AsyncOperation asyncOperation = AsyncOperationManager.CreateOperation(userState);
AsyncOperationContext context = new AsyncOperationContext(asyncOperation, endOperationDelegate, operationCompletedCallback);
Exception error = null;
object[] results = null;
IAsyncResult result = null;
try
{
result = beginOperationDelegate(inValues, onAsyncCallCompleted, context);
if (result.CompletedSynchronously)
{
results = endOperationDelegate(result);
}
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
error = e;
}
if (error != null || result.CompletedSynchronously) /* result cannot be null if error == null */
{
CompleteAsyncCall(context, results, error);
}
}
static void OnAsyncCallCompleted(IAsyncResult result)
{
if (result.CompletedSynchronously)
{
return;
}
AsyncOperationContext context = (AsyncOperationContext)result.AsyncState;
Exception error = null;
object[] results = null;
try
{
results = context.EndDelegate(result);
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
error = e;
}
CompleteAsyncCall(context, results, error);
}
static void CompleteAsyncCall(AsyncOperationContext context, object[] results, Exception error)
{
if (context.CompletionCallback != null)
{
InvokeAsyncCompletedEventArgs e = new InvokeAsyncCompletedEventArgs(results, error, false, context.AsyncOperation.UserSuppliedState);
context.AsyncOperation.PostOperationCompleted(context.CompletionCallback, e);
}
else
{
context.AsyncOperation.OperationCompleted();
}
}
class AsyncOperationContext
{
AsyncOperation asyncOperation;
EndOperationDelegate endDelegate;
SendOrPostCallback completionCallback;
internal AsyncOperationContext(AsyncOperation asyncOperation, EndOperationDelegate endDelegate, SendOrPostCallback completionCallback)
{
this.asyncOperation = asyncOperation;
this.endDelegate = endDelegate;
this.completionCallback = completionCallback;
}
internal AsyncOperation AsyncOperation
{
get
{
return this.asyncOperation;
}
}
internal EndOperationDelegate EndDelegate
{
get
{
return this.endDelegate;
}
}
internal SendOrPostCallback CompletionCallback
{
get
{
return this.completionCallback;
}
}
}
protected class ChannelBase<T> : IClientChannel, IOutputChannel, IRequestChannel, IChannelBaseProxy
where T : class
{
ServiceChannel channel;
System.ServiceModel.Dispatcher.ImmutableClientRuntime runtime;
protected ChannelBase(ClientBase<T> client)
{
if (client.Endpoint.Address == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxChannelFactoryEndpointAddressUri)));
}
ChannelFactory<T> cf = client.ChannelFactory;
cf.EnsureOpened(); // to prevent the NullReferenceException that is thrown if the ChannelFactory is not open when cf.ServiceChannelFactory is accessed.
this.channel = cf.ServiceChannelFactory.CreateServiceChannel(client.Endpoint.Address, client.Endpoint.Address.Uri);
this.channel.InstanceContext = cf.CallbackInstance;
this.runtime = this.channel.ClientRuntime.GetRuntime();
}
[Fx.Tag.SecurityNote(Critical = "Accesses the critical IMethodCallMessage interface.",
Safe = "The implementation of IMethodCallMessage is local and is created locally as weell; i.e. not passed in from Remoting.")]
[SecuritySafeCritical]
protected IAsyncResult BeginInvoke(string methodName, object[] args, AsyncCallback callback, object state)
{
object[] inArgs = new object[args.Length + 2];
Array.Copy(args, inArgs, args.Length);
inArgs[inArgs.Length - 2] = callback;
inArgs[inArgs.Length - 1] = state;
IMethodCallMessage methodCall = new MethodCallMessage(inArgs);
ProxyOperationRuntime op = GetOperationByName(methodName);
object[] ins = op.MapAsyncBeginInputs(methodCall, out callback, out state);
return this.channel.BeginCall(op.Action, op.IsOneWay, op, ins, callback, state);
}
[Fx.Tag.SecurityNote(Critical = "Accesses the critical IMethodCallMessage interface.",
Safe = "The implementation of IMethodCallMessage is local and is created locally as weell; i.e. not passed in from Remoting.")]
[SecuritySafeCritical]
protected object EndInvoke(string methodName, object[] args, IAsyncResult result)
{
object[] inArgs = new object[args.Length + 1];
Array.Copy(args, inArgs, args.Length);
inArgs[inArgs.Length - 1] = result;
IMethodCallMessage methodCall = new MethodCallMessage(inArgs);
ProxyOperationRuntime op = GetOperationByName(methodName);
object[] outs;
op.MapAsyncEndInputs(methodCall, out result, out outs);
object ret = this.channel.EndCall(op.Action, outs, result);
object[] retArgs = op.MapAsyncOutputs(methodCall, outs, ref ret);
if (retArgs != null)
{
Fx.Assert(retArgs.Length == inArgs.Length, "retArgs.Length should be equal to inArgs.Length");
Array.Copy(retArgs, args, args.Length);
}
return ret;
}
System.ServiceModel.Dispatcher.ProxyOperationRuntime GetOperationByName(string methodName)
{
ProxyOperationRuntime op = this.runtime.GetOperationByName(methodName);
if (op == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SFxMethodNotSupported1, methodName)));
}
return op;
}
bool IClientChannel.AllowInitializationUI
{
get { return ((IClientChannel)this.channel).AllowInitializationUI; }
set { ((IClientChannel)this.channel).AllowInitializationUI = value; }
}
bool IClientChannel.DidInteractiveInitialization
{
get { return ((IClientChannel)this.channel).DidInteractiveInitialization; }
}
Uri IClientChannel.Via
{
get { return ((IClientChannel)this.channel).Via; }
}
event EventHandler<UnknownMessageReceivedEventArgs> IClientChannel.UnknownMessageReceived
{
add { ((IClientChannel)this.channel).UnknownMessageReceived += value; }
remove { ((IClientChannel)this.channel).UnknownMessageReceived -= value; }
}
void IClientChannel.DisplayInitializationUI()
{
((IClientChannel)this.channel).DisplayInitializationUI();
}
IAsyncResult IClientChannel.BeginDisplayInitializationUI(AsyncCallback callback, object state)
{
return ((IClientChannel)this.channel).BeginDisplayInitializationUI(callback, state);
}
void IClientChannel.EndDisplayInitializationUI(IAsyncResult result)
{
((IClientChannel)this.channel).EndDisplayInitializationUI(result);
}
bool IContextChannel.AllowOutputBatching
{
get { return ((IContextChannel)this.channel).AllowOutputBatching; }
set { ((IContextChannel)this.channel).AllowOutputBatching = value; }
}
IInputSession IContextChannel.InputSession
{
get { return ((IContextChannel)this.channel).InputSession; }
}
EndpointAddress IContextChannel.LocalAddress
{
get { return ((IContextChannel)this.channel).LocalAddress; }
}
TimeSpan IContextChannel.OperationTimeout
{
get { return ((IContextChannel)this.channel).OperationTimeout; }
set { ((IContextChannel)this.channel).OperationTimeout = value; }
}
IOutputSession IContextChannel.OutputSession
{
get { return ((IContextChannel)this.channel).OutputSession; }
}
EndpointAddress IContextChannel.RemoteAddress
{
get { return ((IContextChannel)this.channel).RemoteAddress; }
}
string IContextChannel.SessionId
{
get { return ((IContextChannel)this.channel).SessionId; }
}
TProperty IChannel.GetProperty<TProperty>()
{
return ((IChannel)this.channel).GetProperty<TProperty>();
}
CommunicationState ICommunicationObject.State
{
get { return ((ICommunicationObject)this.channel).State; }
}
event EventHandler ICommunicationObject.Closed
{
add { ((ICommunicationObject)this.channel).Closed += value; }
remove { ((ICommunicationObject)this.channel).Closed -= value; }
}
event EventHandler ICommunicationObject.Closing
{
add { ((ICommunicationObject)this.channel).Closing += value; }
remove { ((ICommunicationObject)this.channel).Closing -= value; }
}
event EventHandler ICommunicationObject.Faulted
{
add { ((ICommunicationObject)this.channel).Faulted += value; }
remove { ((ICommunicationObject)this.channel).Faulted -= value; }
}
event EventHandler ICommunicationObject.Opened
{
add { ((ICommunicationObject)this.channel).Opened += value; }
remove { ((ICommunicationObject)this.channel).Opened -= value; }
}
event EventHandler ICommunicationObject.Opening
{
add { ((ICommunicationObject)this.channel).Opening += value; }
remove { ((ICommunicationObject)this.channel).Opening -= value; }
}
void ICommunicationObject.Abort()
{
((ICommunicationObject)this.channel).Abort();
}
void ICommunicationObject.Close()
{
((ICommunicationObject)this.channel).Close();
}
void ICommunicationObject.Close(TimeSpan timeout)
{
((ICommunicationObject)this.channel).Close(timeout);
}
IAsyncResult ICommunicationObject.BeginClose(AsyncCallback callback, object state)
{
return ((ICommunicationObject)this.channel).BeginClose(callback, state);
}
IAsyncResult ICommunicationObject.BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
{
return ((ICommunicationObject)this.channel).BeginClose(timeout, callback, state);
}
void ICommunicationObject.EndClose(IAsyncResult result)
{
((ICommunicationObject)this.channel).EndClose(result);
}
void ICommunicationObject.Open()
{
((ICommunicationObject)this.channel).Open();
}
void ICommunicationObject.Open(TimeSpan timeout)
{
((ICommunicationObject)this.channel).Open(timeout);
}
IAsyncResult ICommunicationObject.BeginOpen(AsyncCallback callback, object state)
{
return ((ICommunicationObject)this.channel).BeginOpen(callback, state);
}
IAsyncResult ICommunicationObject.BeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
{
return ((ICommunicationObject)this.channel).BeginOpen(timeout, callback, state);
}
void ICommunicationObject.EndOpen(IAsyncResult result)
{
((ICommunicationObject)this.channel).EndOpen(result);
}
IExtensionCollection<IContextChannel> IExtensibleObject<IContextChannel>.Extensions
{
get { return ((IExtensibleObject<IContextChannel>)this.channel).Extensions; }
}
void IDisposable.Dispose()
{
((IDisposable)this.channel).Dispose();
}
Uri IOutputChannel.Via
{
get { return ((IOutputChannel)this.channel).Via; }
}
EndpointAddress IOutputChannel.RemoteAddress
{
get { return ((IOutputChannel)this.channel).RemoteAddress; }
}
void IOutputChannel.Send(Message message)
{
((IOutputChannel)this.channel).Send(message);
}
void IOutputChannel.Send(Message message, TimeSpan timeout)
{
((IOutputChannel)this.channel).Send(message, timeout);
}
IAsyncResult IOutputChannel.BeginSend(Message message, AsyncCallback callback, object state)
{
return ((IOutputChannel)this.channel).BeginSend(message, callback, state);
}
IAsyncResult IOutputChannel.BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state)
{
return ((IOutputChannel)this.channel).BeginSend(message, timeout, callback, state);
}
void IOutputChannel.EndSend(IAsyncResult result)
{
((IOutputChannel)this.channel).EndSend(result);
}
Uri IRequestChannel.Via
{
get { return ((IRequestChannel)this.channel).Via; }
}
EndpointAddress IRequestChannel.RemoteAddress
{
get { return ((IRequestChannel)this.channel).RemoteAddress; }
}
Message IRequestChannel.Request(Message message)
{
return ((IRequestChannel)this.channel).Request(message);
}
Message IRequestChannel.Request(Message message, TimeSpan timeout)
{
return ((IRequestChannel)this.channel).Request(message, timeout);
}
IAsyncResult IRequestChannel.BeginRequest(Message message, AsyncCallback callback, object state)
{
return ((IRequestChannel)this.channel).BeginRequest(message, callback, state);
}
IAsyncResult IRequestChannel.BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state)
{
return ((IRequestChannel)this.channel).BeginRequest(message, timeout, callback, state);
}
Message IRequestChannel.EndRequest(IAsyncResult result)
{
return ((IRequestChannel)this.channel).EndRequest(result);
}
ServiceChannel IChannelBaseProxy.GetServiceChannel()
{
return this.channel;
}
class MethodCallMessage : IMethodCallMessage
{
readonly object[] args;
public MethodCallMessage(object[] args)
{
this.args = args;
}
public object[] Args
{
get { return this.args; }
}
public int ArgCount
{
get { return this.args.Length; }
}
public LogicalCallContext LogicalCallContext
{
get { return null; }
}
public object GetInArg(int argNum)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
}
public string GetInArgName(int index)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
}
public int InArgCount
{
get
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
}
}
public object[] InArgs
{
get { return this.args; }
}
public object GetArg(int argNum)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
}
public string GetArgName(int index)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
}
public bool HasVarArgs
{
get
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
}
}
public Reflection.MethodBase MethodBase
{
get
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
}
}
public string MethodName
{
get
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
}
}
public object MethodSignature
{
get
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
}
}
public string TypeName
{
get
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
}
}
public string Uri
{
get
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
}
}
public Collections.IDictionary Properties
{
get
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException());
}
}
}
}
}
}
|