using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Microsoft.Extensions.Internal;
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup;
[DebuggerDisplay("{DebuggerToString(),nq}")]
[DebuggerTypeProxy(typeof(ServiceProviderEngineScopeDebugView))]
internal sealed class ServiceProviderEngineScope : IServiceScope, IDisposable, IServiceProvider, IKeyedServiceProvider, IAsyncDisposable, IServiceScopeFactory
{
private sealed class ServiceProviderEngineScopeDebugView
{
private readonly ServiceProviderEngineScope _serviceProvider;
public List<ServiceDescriptor> ServiceDescriptors => new List<ServiceDescriptor>(_serviceProvider.RootProvider.CallSiteFactory.Descriptors);
public List<object> Disposables => new List<object>(_serviceProvider.Disposables);
public bool Disposed => _serviceProvider._disposed;
public bool IsScope => !_serviceProvider.IsRootScope;
public ServiceProviderEngineScopeDebugView(ServiceProviderEngineScope serviceProvider)
{
_serviceProvider = serviceProvider;
}
}
private bool _disposed;
private List<object> _disposables;
internal IList<object> Disposables
{
get
{
IList<object> disposables = _disposables;
return disposables ?? Array.Empty<object>();
}
}
internal Dictionary<ServiceCacheKey, object?> ResolvedServices { get; }
internal bool Disposed => _disposed;
internal object Sync => ResolvedServices;
public bool IsRootScope { get; }
internal ServiceProvider RootProvider { get; }
public IServiceProvider ServiceProvider => this;
public ServiceProviderEngineScope(ServiceProvider provider, bool isRootScope)
{
ResolvedServices = new Dictionary<ServiceCacheKey, object>();
RootProvider = provider;
IsRootScope = isRootScope;
}
public object? GetService(Type serviceType)
{
if (_disposed)
{
ThrowHelper.ThrowObjectDisposedException();
}
return RootProvider.GetService(ServiceIdentifier.FromServiceType(serviceType), this);
}
public object? GetKeyedService(Type serviceType, object? serviceKey)
{
if (_disposed)
{
ThrowHelper.ThrowObjectDisposedException();
}
return RootProvider.GetKeyedService(serviceType, serviceKey, this);
}
public object GetRequiredKeyedService(Type serviceType, object? serviceKey)
{
if (_disposed)
{
ThrowHelper.ThrowObjectDisposedException();
}
return RootProvider.GetRequiredKeyedService(serviceType, serviceKey, this);
}
public IServiceScope CreateScope()
{
return RootProvider.CreateScope();
}
[return: NotNullIfNotNull("service")]
internal object? CaptureDisposable(object? service)
{
if (this == service || (!(service is IDisposable) && !(service is IAsyncDisposable)))
{
return service;
}
bool flag = false;
lock (Sync)
{
if (_disposed)
{
flag = true;
}
else
{
if (_disposables == null)
{
_disposables = new List<object>();
}
_disposables.Add(service);
}
}
if (flag)
{
if (service is IDisposable disposable)
{
disposable.Dispose();
}
else
{
object localService = service;
Task.Run(() => ((IAsyncDisposable)localService).DisposeAsync().AsTask()).GetAwaiter().GetResult();
}
ThrowHelper.ThrowObjectDisposedException();
}
return service;
}
public void Dispose()
{
List<object> list = BeginDispose();
if (list == null)
{
return;
}
int num = list.Count - 1;
while (num >= 0)
{
if (list[num] is IDisposable disposable)
{
disposable.Dispose();
num--;
continue;
}
throw new InvalidOperationException(System.SR.Format(System.SR.AsyncDisposableServiceDispose, TypeNameHelper.GetTypeDisplayName(list[num])));
}
}
public ValueTask DisposeAsync()
{
List<object> list = BeginDispose();
if (list != null)
{
try
{
for (int num = list.Count - 1; num >= 0; num--)
{
object obj = list[num];
if (obj is IAsyncDisposable asyncDisposable)
{
ValueTask vt2 = asyncDisposable.DisposeAsync();
if (!vt2.IsCompletedSuccessfully)
{
return Await(num, vt2, list);
}
vt2.GetAwaiter().GetResult();
}
else
{
((IDisposable)obj).Dispose();
}
}
}
catch (Exception exception)
{
return new ValueTask(Task.FromException(exception));
}
}
return default(ValueTask);
static async ValueTask Await(int i, ValueTask vt, List<object> toDispose)
{
await vt.ConfigureAwait(continueOnCapturedContext: false);
i--;
while (i >= 0)
{
object obj2 = toDispose[i];
if (obj2 is IAsyncDisposable asyncDisposable2)
{
await asyncDisposable2.DisposeAsync().ConfigureAwait(continueOnCapturedContext: false);
}
else
{
((IDisposable)obj2).Dispose();
}
i--;
}
}
}
private List<object> BeginDispose()
{
lock (Sync)
{
if (_disposed)
{
return null;
}
DependencyInjectionEventSource.Log.ScopeDisposed(RootProvider.GetHashCode(), ResolvedServices.Count, _disposables?.Count ?? 0);
_disposed = true;
}
if (IsRootScope && !RootProvider.IsDisposed())
{
RootProvider.Dispose();
}
return _disposables;
}
internal string DebuggerToString()
{
string text = $"ServiceDescriptors = {RootProvider.CallSiteFactory.Descriptors.Length}";
if (!IsRootScope)
{
text += ", IsScope = true";
}
if (_disposed)
{
text += ", Disposed = true";
}
return text;
}
}