| | | 1 | | // Licensed to the .NET Foundation under one or more agreements. |
| | | 2 | | // The .NET Foundation licenses this file to you under the MIT license. |
| | | 3 | | |
| | | 4 | | using System; |
| | | 5 | | using System.Reflection; |
| | | 6 | | using System.Threading.Tasks; |
| | | 7 | | using CoreWCF.Channels; |
| | | 8 | | using CoreWCF.Diagnostics; |
| | | 9 | | using CoreWCF.Runtime; |
| | | 10 | | |
| | | 11 | | namespace CoreWCF.Dispatcher |
| | | 12 | | { |
| | | 13 | | internal class InstanceBehavior |
| | | 14 | | { |
| | | 15 | | private const BindingFlags DefaultBindingFlags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.P |
| | | 16 | | private readonly IInstanceContextInitializer[] _initializers; |
| | | 17 | | private readonly IInstanceProvider _provider; |
| | | 18 | | private readonly InstanceContext _singleton; |
| | | 19 | | private readonly ImmutableDispatchRuntime _immutableRuntime; |
| | | 20 | | private readonly Type _serviceType; |
| | | 21 | | |
| | 661 | 22 | | internal InstanceBehavior(DispatchRuntime dispatch, ImmutableDispatchRuntime immutableRuntime) |
| | | 23 | | { |
| | 661 | 24 | | _immutableRuntime = immutableRuntime; |
| | 661 | 25 | | _serviceType = dispatch.Type; |
| | 661 | 26 | | _initializers = EmptyArray<IInstanceContextInitializer>.ToArray(dispatch.InstanceContextInitializers); |
| | 661 | 27 | | _provider = dispatch.InstanceProvider; |
| | 661 | 28 | | _singleton = dispatch.SingletonInstanceContext; |
| | 661 | 29 | | InstanceContextProvider = dispatch.InstanceContextProvider; |
| | | 30 | | |
| | 661 | 31 | | if (_provider == null) |
| | | 32 | | { |
| | 125 | 33 | | bool hasDefaultConstructor = dispatch.Type != null && InvokerUtil.HasDefaultConstructor(dispatch.Type); |
| | 125 | 34 | | if (_singleton == null) |
| | | 35 | | { |
| | 0 | 36 | | if (dispatch.Type != null && (dispatch.Type.GetTypeInfo().IsAbstract || dispatch.Type.GetTypeInfo(). |
| | | 37 | | { |
| | 0 | 38 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxSe |
| | | 39 | | } |
| | | 40 | | |
| | 0 | 41 | | if (!hasDefaultConstructor) |
| | | 42 | | { |
| | 0 | 43 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( |
| | 0 | 44 | | SR.Format(SR.SFxNoDefaultConstructor, dispatch.Type?.FullName ?? "Unknown"))); |
| | | 45 | | } |
| | | 46 | | } |
| | | 47 | | |
| | 125 | 48 | | if (!hasDefaultConstructor) |
| | | 49 | | { |
| | 23 | 50 | | if (_singleton == null || !_singleton.IsWellKnown) |
| | | 51 | | { |
| | 0 | 52 | | CreateInstanceDelegate creator = InvokerUtil.GenerateCreateInstanceDelegate(dispatch.Type); |
| | 0 | 53 | | _provider = new InstanceProvider(creator); |
| | | 54 | | } |
| | | 55 | | } |
| | | 56 | | } |
| | | 57 | | |
| | 661 | 58 | | if (_singleton != null) |
| | | 59 | | { |
| | 125 | 60 | | _singleton.Behavior = this; |
| | | 61 | | } |
| | 661 | 62 | | } |
| | | 63 | | |
| | 9783 | 64 | | internal IInstanceContextProvider InstanceContextProvider { get; } |
| | | 65 | | |
| | | 66 | | internal void AfterReply(ref MessageRpc rpc, ErrorBehavior error) |
| | | 67 | | { |
| | 2538 | 68 | | InstanceContext context = rpc.InstanceContext; |
| | | 69 | | |
| | 2538 | 70 | | if (context != null) |
| | | 71 | | { |
| | | 72 | | try |
| | | 73 | | { |
| | 2536 | 74 | | if (rpc.Operation.ReleaseInstanceAfterCall) |
| | | 75 | | { |
| | 0 | 76 | | if (context.State == CommunicationState.Opened) |
| | | 77 | | { |
| | 0 | 78 | | context.ReleaseServiceInstance(); |
| | | 79 | | } |
| | | 80 | | } |
| | 2536 | 81 | | } |
| | 0 | 82 | | catch (Exception e) |
| | | 83 | | { |
| | 0 | 84 | | if (Fx.IsFatal(e)) |
| | | 85 | | { |
| | 0 | 86 | | throw; |
| | | 87 | | } |
| | 0 | 88 | | error.HandleError(e); |
| | 0 | 89 | | } |
| | | 90 | | |
| | | 91 | | try |
| | | 92 | | { |
| | 2536 | 93 | | context.UnbindRpc(rpc); |
| | 2536 | 94 | | } |
| | 0 | 95 | | catch (Exception e) |
| | | 96 | | { |
| | 0 | 97 | | if (Fx.IsFatal(e)) |
| | | 98 | | { |
| | 0 | 99 | | throw; |
| | | 100 | | } |
| | 0 | 101 | | error.HandleError(e); |
| | 0 | 102 | | } |
| | | 103 | | } |
| | 2538 | 104 | | } |
| | | 105 | | |
| | | 106 | | internal bool CanUnload(InstanceContext instanceContext) |
| | | 107 | | { |
| | 2332 | 108 | | if (InstanceContextProviderBase.IsProviderSingleton(InstanceContextProvider)) |
| | | 109 | | { |
| | 0 | 110 | | return false; |
| | | 111 | | } |
| | | 112 | | |
| | 2332 | 113 | | if (InstanceContextProviderBase.IsProviderPerCall(InstanceContextProvider) || |
| | 2332 | 114 | | InstanceContextProviderBase.IsProviderSessionful(InstanceContextProvider)) |
| | | 115 | | { |
| | 2332 | 116 | | return true; |
| | | 117 | | } |
| | | 118 | | |
| | | 119 | | //User provided InstanceContextProvider. Call the provider to check for idle. |
| | 0 | 120 | | if (!InstanceContextProvider.IsIdle(instanceContext)) |
| | | 121 | | { |
| | 0 | 122 | | InstanceContextProvider.NotifyIdle(InstanceContext.NotifyIdleCallback, instanceContext); |
| | 0 | 123 | | return false; |
| | | 124 | | } |
| | 0 | 125 | | return true; |
| | | 126 | | } |
| | | 127 | | |
| | | 128 | | internal async Task EnsureInstanceContextAsync(MessageRpc rpc) |
| | | 129 | | { |
| | 2536 | 130 | | if (rpc.InstanceContext == null) |
| | | 131 | | { |
| | 2391 | 132 | | rpc.InstanceContext = new InstanceContext(rpc.Host, false) |
| | 2391 | 133 | | { |
| | 2391 | 134 | | ServiceThrottle = rpc.ChannelHandler.InstanceContextServiceThrottle |
| | 2391 | 135 | | }; |
| | 2391 | 136 | | rpc.MessageRpcOwnsInstanceContextThrottle = false; |
| | | 137 | | } |
| | | 138 | | |
| | 2536 | 139 | | rpc.OperationContext.SetInstanceContext(rpc.InstanceContext); |
| | 2536 | 140 | | rpc.InstanceContext.Behavior = this; |
| | | 141 | | |
| | 2536 | 142 | | if (rpc.InstanceContext.State == CommunicationState.Created) |
| | | 143 | | { |
| | 2391 | 144 | | Task openTask = null; |
| | 2391 | 145 | | lock (rpc.InstanceContext.ThisLock) |
| | | 146 | | { |
| | 2391 | 147 | | if (rpc.InstanceContext.State == CommunicationState.Created) |
| | | 148 | | { |
| | 2391 | 149 | | var helper = new TimeoutHelper(rpc.Channel.CloseTimeout); |
| | | 150 | | // awaiting the task outside the lock is safe as OpenAsync will transition the state away from C |
| | | 151 | | // it returns an uncompleted Task. |
| | 2391 | 152 | | openTask = rpc.InstanceContext.OpenAsync(helper.GetCancellationToken()); |
| | | 153 | | Fx.Assert(rpc.InstanceContext.State != CommunicationState.Created, "InstanceContext.OpenAsync sh |
| | | 154 | | } |
| | 2391 | 155 | | } |
| | | 156 | | |
| | 2391 | 157 | | await openTask; |
| | | 158 | | } |
| | | 159 | | |
| | 2536 | 160 | | rpc.InstanceContext.BindRpc(rpc); |
| | 2536 | 161 | | } |
| | | 162 | | |
| | | 163 | | private static ConstructorInfo GetConstructor(Type type) |
| | | 164 | | { |
| | 0 | 165 | | foreach (ConstructorInfo constructor in type.GetConstructors(DefaultBindingFlags)) |
| | | 166 | | { |
| | 0 | 167 | | if (constructor.GetParameters().Length == 0) |
| | | 168 | | { |
| | 0 | 169 | | return constructor; |
| | | 170 | | } |
| | | 171 | | } |
| | 0 | 172 | | return null; |
| | | 173 | | } |
| | | 174 | | |
| | | 175 | | internal object GetInstance(InstanceContext instanceContext) |
| | | 176 | | { |
| | 0 | 177 | | if (_provider == null) |
| | | 178 | | { |
| | 0 | 179 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException( |
| | 0 | 180 | | SR.Format(SR.SFxNoDefaultConstructor, _serviceType?.FullName ?? "Unknown"))); |
| | | 181 | | } |
| | | 182 | | |
| | 0 | 183 | | return _provider.GetInstance(instanceContext); |
| | | 184 | | } |
| | | 185 | | |
| | | 186 | | internal object GetInstance(InstanceContext instanceContext, Message request) |
| | | 187 | | { |
| | 2391 | 188 | | if (_provider == null) |
| | | 189 | | { |
| | 0 | 190 | | throw TraceUtility.ThrowHelperError(new InvalidOperationException( |
| | 0 | 191 | | SR.Format(SR.SFxNoDefaultConstructor, _serviceType?.FullName ?? "Unknown")), request); |
| | | 192 | | } |
| | | 193 | | |
| | 2391 | 194 | | return _provider.GetInstance(instanceContext, request); |
| | | 195 | | } |
| | | 196 | | |
| | | 197 | | internal void Initialize(InstanceContext instanceContext) |
| | | 198 | | { |
| | 2473 | 199 | | OperationContext current = OperationContext.Current; |
| | 2473 | 200 | | Message message = current?.IncomingMessage; |
| | | 201 | | |
| | 2473 | 202 | | if (current != null && current.InternalServiceChannel != null) |
| | | 203 | | { |
| | 2391 | 204 | | IContextChannel transparentProxy = (IContextChannel)current.InternalServiceChannel.Proxy; |
| | 2391 | 205 | | InstanceContextProvider.InitializeInstanceContext(instanceContext, message, transparentProxy); |
| | | 206 | | } |
| | | 207 | | |
| | 4946 | 208 | | for (int i = 0; i < _initializers.Length; i++) |
| | | 209 | | { |
| | 0 | 210 | | _initializers[i].Initialize(instanceContext, message); |
| | | 211 | | } |
| | 2473 | 212 | | } |
| | | 213 | | |
| | | 214 | | internal void EnsureServiceInstance(MessageRpc rpc) |
| | | 215 | | { |
| | 2536 | 216 | | if (rpc.Operation.ReleaseInstanceBeforeCall) |
| | | 217 | | { |
| | 0 | 218 | | rpc.InstanceContext.ReleaseServiceInstance(); |
| | | 219 | | } |
| | | 220 | | |
| | | 221 | | //if (TD.GetServiceInstanceStartIsEnabled()) |
| | | 222 | | //{ |
| | | 223 | | // TD.GetServiceInstanceStart(rpc.EventTraceActivity); |
| | | 224 | | //} |
| | | 225 | | |
| | 2536 | 226 | | rpc.Instance = rpc.InstanceContext.GetServiceInstance(rpc.Request); |
| | | 227 | | |
| | | 228 | | //if (TD.GetServiceInstanceStopIsEnabled()) |
| | | 229 | | //{ |
| | | 230 | | // TD.GetServiceInstanceStop(rpc.EventTraceActivity); |
| | | 231 | | //} |
| | 2535 | 232 | | } |
| | | 233 | | |
| | | 234 | | internal void ReleaseInstance(InstanceContext instanceContext, object instance) |
| | | 235 | | { |
| | 2389 | 236 | | if (_provider != null) |
| | | 237 | | { |
| | | 238 | | try |
| | | 239 | | { |
| | 2389 | 240 | | _provider.ReleaseInstance(instanceContext, instance); |
| | 2389 | 241 | | } |
| | 0 | 242 | | catch (Exception e) |
| | | 243 | | { |
| | 0 | 244 | | if (Fx.IsFatal(e)) |
| | | 245 | | { |
| | 0 | 246 | | throw; |
| | | 247 | | } |
| | 0 | 248 | | _immutableRuntime.ErrorBehavior.HandleError(e); |
| | 0 | 249 | | } |
| | | 250 | | } |
| | 2389 | 251 | | } |
| | | 252 | | } |
| | | 253 | | |
| | | 254 | | internal class InstanceProvider : IInstanceProvider |
| | | 255 | | { |
| | | 256 | | private readonly CreateInstanceDelegate _creator; |
| | | 257 | | |
| | | 258 | | internal InstanceProvider(CreateInstanceDelegate creator) |
| | | 259 | | { |
| | | 260 | | _creator = creator ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(creator)); |
| | | 261 | | } |
| | | 262 | | |
| | | 263 | | public object GetInstance(InstanceContext instanceContext) |
| | | 264 | | { |
| | | 265 | | return _creator(); |
| | | 266 | | } |
| | | 267 | | |
| | | 268 | | public object GetInstance(InstanceContext instanceContext, Message message) |
| | | 269 | | { |
| | | 270 | | return _creator(); |
| | | 271 | | } |
| | | 272 | | |
| | | 273 | | public void ReleaseInstance(InstanceContext instanceContext, object instance) |
| | | 274 | | { |
| | | 275 | | if (instance is IDisposable dispose) |
| | | 276 | | { |
| | | 277 | | dispose.Dispose(); |
| | | 278 | | } |
| | | 279 | | } |
| | | 280 | | } |
| | | 281 | | } |