Add support for "Scoped" lifetime (#31)
This commit is contained in:
Родитель
d41299ea39
Коммит
03c1aa801f
|
@ -0,0 +1,4 @@
|
|||
[*.cs]
|
||||
|
||||
# Default severity for all analyzer diagnostics
|
||||
dotnet_analyzer_diagnostic.severity = silent
|
28
README.md
28
README.md
|
@ -27,6 +27,8 @@ A Dependency Injection (DI) Container provides functionality and automates many
|
|||
This API mirrors as close as possible the official .NET
|
||||
[DependencyInjection](https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection). Exceptions are mainly derived from the lack of generics support in .NET nanoFramework.
|
||||
|
||||
The .NET nanoFramework [Generic Host](https://github.com/nanoframework/nanoFramework.Hosting) provides convenience methods for creating dependency injection (DI) application containers with preconfigured defaults.
|
||||
|
||||
## Usage
|
||||
|
||||
### Service Collection
|
||||
|
@ -91,6 +93,20 @@ var service = (RootObject)serviceProvider.GetService(typeof(RootObject));
|
|||
service.ServiceObject.Three = "3";
|
||||
```
|
||||
|
||||
Create a scoped Service Provider providing convient access to crate and distroy scoped object.
|
||||
|
||||
```csharp
|
||||
var serviceProvider = new ServiceCollection()
|
||||
.AddScoped(typeof(typeof(ServiceObject))
|
||||
.BuildServiceProvider();
|
||||
|
||||
using (var scope = serviceProvider.CreateScope())
|
||||
{
|
||||
var service = scope.ServiceProvider.GetServices(typeof(ServiceObject));
|
||||
service.ServiceObject.Three = "3";
|
||||
}
|
||||
```
|
||||
|
||||
## Activator Utilities
|
||||
|
||||
An instance of an object can be created by calling its constructor with any dependencies resolved through the service provider. Automatically instantiate a type with constructor arguments provided from an IServiceProvider without having to register the type with the DI Container.
|
||||
|
@ -116,6 +132,18 @@ var serviceProvider = new ServiceCollection()
|
|||
.BuildServiceProvider(new ServiceProviderOptions() { ValidateOnBuild = true });
|
||||
```
|
||||
|
||||
|
||||
### Validate Scopes
|
||||
|
||||
A check verifying that scoped services never gets resolved from root provider. Validate on build is configured false by default.
|
||||
|
||||
```csharp
|
||||
var serviceProvider = new ServiceCollection()
|
||||
.AddSingleton(typeof(IServiceObject), typeof(ServiceObject))
|
||||
.BuildServiceProvider(new ServiceProviderOptions() { ValidateScopes = true });
|
||||
```
|
||||
|
||||
|
||||
## Example Application Container
|
||||
|
||||
```csharp
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// Copyright (c) .NET Foundation and Contributors
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace nanoFramework.DependencyInjection
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines scope for <see cref="IServiceProvider"/>.
|
||||
/// </summary>
|
||||
public interface IServiceScope : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="IServiceProvider"/> used to resolve dependencies from the scope.
|
||||
/// </summary>
|
||||
IServiceProvider ServiceProvider { get; }
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ using System.Collections;
|
|||
namespace nanoFramework.DependencyInjection
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for <see cref="ServiceCollection"/>.
|
||||
/// Extensions for <see cref="IServiceCollection"/>.
|
||||
/// </summary>
|
||||
public static class ServiceCollectionServiceExtensions
|
||||
{
|
||||
|
@ -123,6 +123,49 @@ namespace nanoFramework.DependencyInjection
|
|||
return services.AddTransient(serviceType, serviceType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a scoped service of the type specified in <paramref name="serviceType"/> with an
|
||||
/// implementation of the type specified in <paramref name="implementationType"/> to the
|
||||
/// specified <see cref="IServiceCollection"/>.
|
||||
/// </summary>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
|
||||
/// <param name="serviceType">The type of the service to register.</param>
|
||||
/// <param name="implementationType">The implementation type of the service.</param>
|
||||
/// <returns>A reference to this instance after the operation has completed.</returns>
|
||||
/// <seealso cref="ServiceLifetime.Scoped"/>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="services"/> can't be <see langword="null"/>.</exception>
|
||||
public static IServiceCollection AddScoped(this IServiceCollection services, Type serviceType, Type implementationType)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
var descriptor = new ServiceDescriptor(serviceType, implementationType, ServiceLifetime.Scoped);
|
||||
services.Add(descriptor);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a scoped service of the type specified in <paramref name="serviceType"/> to the
|
||||
/// specified <see cref="IServiceCollection"/>.
|
||||
/// </summary>
|
||||
/// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
|
||||
/// <param name="serviceType">The type of the service to register and the implementation to use.</param>
|
||||
/// <returns>A reference to this instance after the operation has completed.</returns>
|
||||
/// <seealso cref="ServiceLifetime.Scoped"/>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="services"/> can't be <see langword="null"/>.</exception>
|
||||
public static IServiceCollection AddScoped(this IServiceCollection services, Type serviceType)
|
||||
{
|
||||
if (services == null)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
return services.AddScoped(serviceType, serviceType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified <paramref name="descriptor"/> to the <paramref name="collection"/> if the
|
||||
/// service type hasn't already been registered.
|
||||
|
|
|
@ -18,6 +18,11 @@ namespace nanoFramework.DependencyInjection
|
|||
/// <summary>
|
||||
/// Specifies that a new instance of the service will be created every time it is requested.
|
||||
/// </summary>
|
||||
Transient
|
||||
Transient,
|
||||
|
||||
/// <summary>
|
||||
/// Specifies that a single instance of the service will be created within a scope.
|
||||
/// </summary>
|
||||
Scoped
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,8 @@ namespace nanoFramework.DependencyInjection
|
|||
public sealed class ServiceProvider : IServiceProvider, IDisposable
|
||||
{
|
||||
private bool _disposed;
|
||||
internal ServiceProviderEngine _engine;
|
||||
|
||||
internal ServiceProviderEngine Engine { get; }
|
||||
|
||||
internal ServiceProvider(IServiceCollection services, ServiceProviderOptions options)
|
||||
{
|
||||
|
@ -29,9 +30,10 @@ namespace nanoFramework.DependencyInjection
|
|||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
_engine = GetEngine();
|
||||
_engine.Services = services;
|
||||
_engine.Services.Add(new ServiceDescriptor(typeof(IServiceProvider), this));
|
||||
Engine = GetEngine();
|
||||
Engine.Services = services;
|
||||
Engine.Services.Add(new ServiceDescriptor(typeof(IServiceProvider), this));
|
||||
Engine.Options = options;
|
||||
|
||||
if (options.ValidateOnBuild)
|
||||
{
|
||||
|
@ -41,7 +43,7 @@ namespace nanoFramework.DependencyInjection
|
|||
{
|
||||
try
|
||||
{
|
||||
_engine.ValidateService(descriptor);
|
||||
Engine.ValidateService(descriptor);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -65,7 +67,7 @@ namespace nanoFramework.DependencyInjection
|
|||
throw new ObjectDisposedException();
|
||||
}
|
||||
|
||||
return _engine.GetService(serviceType);
|
||||
return Engine.GetService(serviceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
@ -76,7 +78,13 @@ namespace nanoFramework.DependencyInjection
|
|||
throw new ObjectDisposedException();
|
||||
}
|
||||
|
||||
return _engine.GetService(serviceType);
|
||||
return Engine.GetService(serviceType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IServiceScope CreateScope()
|
||||
{
|
||||
return new ServiceProviderEngineScope(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
@ -88,7 +96,7 @@ namespace nanoFramework.DependencyInjection
|
|||
}
|
||||
|
||||
_disposed = true;
|
||||
_engine.DisposeServices();
|
||||
Engine.DisposeServices();
|
||||
}
|
||||
|
||||
private ServiceProviderEngine GetEngine()
|
||||
|
|
|
@ -23,6 +23,11 @@ namespace nanoFramework.DependencyInjection
|
|||
/// </summary>
|
||||
internal IServiceCollection Services { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ServiceProvider Options instance
|
||||
/// </summary>
|
||||
internal ServiceProviderOptions Options { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Validate service by attempting to activate all dependent services.
|
||||
/// </summary>
|
||||
|
@ -49,9 +54,10 @@ namespace nanoFramework.DependencyInjection
|
|||
/// Gets the last added service object of the specified type.
|
||||
/// </summary>
|
||||
/// <param name="serviceType">An object that specifies the type of service object to get.</param>
|
||||
internal object GetService(Type serviceType)
|
||||
/// <param name="scopeServices">Services collection from current scope.</param>
|
||||
internal object GetService(Type serviceType, IServiceCollection scopeServices = null)
|
||||
{
|
||||
var services = GetServiceObjects(serviceType);
|
||||
var services = GetServiceObjects(serviceType, scopeServices);
|
||||
|
||||
if (services.Length == 0)
|
||||
{
|
||||
|
@ -66,9 +72,10 @@ namespace nanoFramework.DependencyInjection
|
|||
/// Gets the service objects of the specified type.
|
||||
/// </summary>
|
||||
/// <param name="serviceType">An object that specifies the type of service object to get.</param>
|
||||
/// <param name="scopeServices">Services collection from current scope.</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="serviceType"/> can't be <see langword="null"/>.</exception>
|
||||
/// <exception cref="ArgumentException"><paramref name="serviceType"/> can't be empty.</exception>
|
||||
internal object[] GetService(Type[] serviceType)
|
||||
internal object[] GetService(Type[] serviceType, IServiceCollection scopeServices = null)
|
||||
{
|
||||
if (serviceType == null)
|
||||
{
|
||||
|
@ -83,7 +90,7 @@ namespace nanoFramework.DependencyInjection
|
|||
// optimized for single item service type
|
||||
if (serviceType.Length == 1)
|
||||
{
|
||||
var services = GetServiceObjects(serviceType[0]);
|
||||
var services = GetServiceObjects(serviceType[0], scopeServices);
|
||||
|
||||
if (services.Length > 0)
|
||||
{
|
||||
|
@ -98,7 +105,7 @@ namespace nanoFramework.DependencyInjection
|
|||
|
||||
foreach (Type type in serviceType)
|
||||
{
|
||||
var services = GetServiceObjects(type);
|
||||
var services = GetServiceObjects(type, scopeServices);
|
||||
|
||||
if (services.Length > 0)
|
||||
{
|
||||
|
@ -118,29 +125,41 @@ namespace nanoFramework.DependencyInjection
|
|||
/// Gets the service objects of the specified type.
|
||||
/// </summary>
|
||||
/// <param name="serviceType">An object that specifies the type of service object to get.</param>
|
||||
private object[] GetServiceObjects(Type serviceType)
|
||||
/// <param name="scopeServices">Services collection from current scope.</param>
|
||||
private object[] GetServiceObjects(Type serviceType, IServiceCollection scopeServices)
|
||||
{
|
||||
ArrayList services = new ArrayList();
|
||||
|
||||
if (scopeServices != null)
|
||||
{
|
||||
foreach (ServiceDescriptor descriptor in scopeServices)
|
||||
{
|
||||
if (descriptor.ServiceType != serviceType) continue;
|
||||
|
||||
descriptor.ImplementationInstance ??= Resolve(descriptor.ImplementationType);
|
||||
services.Add(descriptor.ImplementationInstance);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (ServiceDescriptor descriptor in Services)
|
||||
{
|
||||
if (descriptor.ServiceType == serviceType)
|
||||
if (descriptor.ServiceType != serviceType) continue;
|
||||
|
||||
switch (descriptor.Lifetime)
|
||||
{
|
||||
if (descriptor.Lifetime == ServiceLifetime.Singleton
|
||||
&& descriptor.ImplementationInstance != null)
|
||||
{
|
||||
case ServiceLifetime.Singleton:
|
||||
descriptor.ImplementationInstance ??= Resolve(descriptor.ImplementationType);
|
||||
services.Add(descriptor.ImplementationInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
var instance = Resolve(descriptor.ImplementationType);
|
||||
if (descriptor.Lifetime != ServiceLifetime.Transient)
|
||||
{
|
||||
descriptor.ImplementationInstance = instance;
|
||||
}
|
||||
break;
|
||||
|
||||
services.Add(instance);
|
||||
}
|
||||
case ServiceLifetime.Transient:
|
||||
services.Add(Resolve(descriptor.ImplementationType));
|
||||
break;
|
||||
|
||||
case ServiceLifetime.Scoped:
|
||||
if (scopeServices == null && Options.ValidateScopes)
|
||||
throw new InvalidOperationException();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
//
|
||||
// Copyright (c) .NET Foundation and Contributors
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace nanoFramework.DependencyInjection
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="System.IDisposable.Dispose"/> method ends the scope lifetime. Once Dispose
|
||||
/// is called, any scoped services that have been resolved from
|
||||
/// <see cref="IServiceProvider"/> will be disposed.
|
||||
/// </summary>
|
||||
internal sealed class ServiceProviderEngineScope : IServiceScope, IServiceProvider
|
||||
{
|
||||
private bool _disposed;
|
||||
|
||||
private readonly IServiceCollection _scopeServices = new ServiceCollection();
|
||||
|
||||
/// <summary>
|
||||
/// The root service provider used to resolve dependencies from the scope.
|
||||
/// </summary>
|
||||
internal ServiceProvider RootProvider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates instance of <see cref="ServiceProviderEngineScope"/>.
|
||||
/// </summary>
|
||||
/// <param name="provider">The root service provider used to resolve dependencies from the scope.</param>
|
||||
internal ServiceProviderEngineScope(ServiceProvider provider)
|
||||
{
|
||||
RootProvider = provider;
|
||||
|
||||
CloneScopeServices();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IServiceProvider"/> resolved from the scope.
|
||||
/// </summary>
|
||||
public IServiceProvider ServiceProvider => this;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public object GetService(Type serviceType)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException();
|
||||
}
|
||||
|
||||
return RootProvider.Engine.GetService(serviceType, _scopeServices);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public object[] GetService(Type[] serviceType)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException();
|
||||
}
|
||||
|
||||
return RootProvider.Engine.GetService(serviceType, _scopeServices);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IServiceScope CreateScope()
|
||||
{
|
||||
return new ServiceProviderEngineScope(RootProvider);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
DisposeServices();
|
||||
}
|
||||
|
||||
private void CloneScopeServices()
|
||||
{
|
||||
foreach (ServiceDescriptor descriptor in RootProvider.Engine.Services)
|
||||
{
|
||||
if (descriptor.Lifetime == ServiceLifetime.Scoped)
|
||||
{
|
||||
_scopeServices.Add(new ServiceDescriptor(
|
||||
descriptor.ServiceType, descriptor.ImplementationType, ServiceLifetime.Scoped));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DisposeServices()
|
||||
{
|
||||
for (int index = _scopeServices.Count - 1; index >= 0; index--)
|
||||
{
|
||||
if (_scopeServices[index].ImplementationInstance is IDisposable disposable)
|
||||
{
|
||||
#pragma warning disable S3966 //services must be disposed explicitly, otherwise ServiceRegisteredWithScopeIsDisposedWhenScopeIsDisposed test fails
|
||||
disposable.Dispose();
|
||||
#pragma warning restore S3966
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,9 +15,13 @@ namespace nanoFramework.DependencyInjection
|
|||
// Avoid allocating objects in the default case
|
||||
internal static readonly ServiceProviderOptions Default = new ServiceProviderOptions();
|
||||
|
||||
/// <summary>
|
||||
/// <see langword="true"/> to perform check verifying that scoped services never gets resolved from root provider; otherwise <see langword="false"/>. Defaults to <see langword="false"/>.
|
||||
/// </summary>
|
||||
public bool ValidateScopes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <see langword="true"/> to perform check verifying that all services can be created during BuildServiceProvider call; otherwise <see langword="false"/>. Defaults to <see langword="false"/>.
|
||||
/// NOTE: this check doesn't verify open generics services.
|
||||
/// </summary>
|
||||
public bool ValidateOnBuild { get; set; }
|
||||
}
|
||||
|
|
|
@ -20,6 +20,11 @@ namespace nanoFramework.DependencyInjection
|
|||
/// <returns>An array of services of type <paramref name="serviceType"/>.</returns>
|
||||
public static object[] GetServices(this IServiceProvider provider, Type serviceType)
|
||||
{
|
||||
if (provider == null)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
return provider.GetService(new Type[] { serviceType });
|
||||
}
|
||||
|
||||
|
@ -74,5 +79,23 @@ namespace nanoFramework.DependencyInjection
|
|||
|
||||
return service;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ServiceScope"/> that can be used to resolve scoped services.
|
||||
/// </summary>
|
||||
/// <param name="provider">The <see cref="IServiceProvider"/> to create the scope from.</param>
|
||||
/// <returns>An <see cref="ServiceScope"/> that can be used to resolve scoped services.</returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="provider"/>can't be <see langword="null"/>.</exception>
|
||||
public static IServiceScope CreateScope(this IServiceProvider provider)
|
||||
{
|
||||
if (provider == null)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
var service = (ServiceProvider)provider.GetRequiredService(typeof(IServiceProvider));
|
||||
|
||||
return new ServiceScope(service.CreateScope());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// Copyright (c) .NET Foundation and Contributors
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace nanoFramework.DependencyInjection
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="IServiceScope" /> implementation that implements <see cref="IDisposable" />.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{ServiceProvider,nq}")]
|
||||
public readonly struct ServiceScope : IServiceScope
|
||||
{
|
||||
private readonly IServiceScope _serviceScope;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ServiceScope"/> struct.
|
||||
/// Wraps an instance of <see cref="IServiceScope" />.
|
||||
/// </summary>
|
||||
/// <param name="serviceScope">The <see cref="IServiceScope"/> instance to wrap.</param>
|
||||
public ServiceScope(IServiceScope serviceScope)
|
||||
{
|
||||
if (serviceScope == null)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
_serviceScope = serviceScope;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IServiceProvider ServiceProvider => _serviceScope.ServiceProvider;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
_serviceScope.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,4 +28,4 @@ namespace System
|
|||
/// </returns>
|
||||
object[] GetService(Type[] serviceType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,10 @@
|
|||
<ItemGroup>
|
||||
<Compile Include="DependencyInjection\ActivatorUtilities.cs" />
|
||||
<Compile Include="DependencyInjection\IServiceCollection.cs" />
|
||||
<Compile Include="DependencyInjection\ServiceScope.cs" />
|
||||
<Compile Include="DependencyInjection\IServiceScope.cs" />
|
||||
<Compile Include="DependencyInjection\ServiceProviderEngine.cs" />
|
||||
<Compile Include="DependencyInjection\ServiceProviderEngineScope.cs" />
|
||||
<Compile Include="DependencyInjection\TypeExtensions.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="DependencyInjection\ServiceCollection.cs" />
|
||||
|
@ -48,6 +51,7 @@
|
|||
<Compile Include="System\IServiceProvider.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\.editorconfig" Link=".editorconfig" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -290,5 +290,110 @@ namespace nanoFramework.DependencyInjection.UnitTests
|
|||
|
||||
Assert.IsNull(service);
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void ServiceRegisteredWithScopedReturnsSameInstanceWithinScope()
|
||||
{
|
||||
var serviceProvider = new ServiceCollection()
|
||||
.AddScoped(typeof(IFakeService), typeof(FakeService))
|
||||
.BuildServiceProvider();
|
||||
|
||||
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
var service1 = scope.ServiceProvider.GetService(typeof(IFakeService));
|
||||
var service2 = scope.ServiceProvider.GetService(typeof(IFakeService));
|
||||
|
||||
Assert.AreSame(service1, service2);
|
||||
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ServiceRegisteredWithScopedReturnsDifferentInstanceOutsideScope()
|
||||
{
|
||||
var serviceProvider = new ServiceCollection()
|
||||
.AddScoped(typeof(IFakeService), typeof(FakeService))
|
||||
.BuildServiceProvider();
|
||||
|
||||
|
||||
using var scope1 = serviceProvider.CreateScope();
|
||||
using var scope2 = serviceProvider.CreateScope();
|
||||
var service1 = scope1.ServiceProvider.GetService(typeof(IFakeService));
|
||||
var service2 = scope2.ServiceProvider.GetService(typeof(IFakeService));
|
||||
|
||||
Assert.AreNotSame(service1, service2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ServiceRegisteredWithScopedIsDisposedWhenScopeIsDisposed()
|
||||
{
|
||||
var serviceProvider = new ServiceCollection()
|
||||
.AddScoped(typeof(IFakeService), typeof(FakeService))
|
||||
.BuildServiceProvider();
|
||||
|
||||
|
||||
FakeService service1, service2;
|
||||
using (var scope1 = serviceProvider.CreateScope())
|
||||
{
|
||||
using (var scope2 = serviceProvider.CreateScope())
|
||||
{
|
||||
service1 = (FakeService)scope1.ServiceProvider.GetService(typeof(IFakeService));
|
||||
service2 = (FakeService)scope2.ServiceProvider.GetService(typeof(IFakeService));
|
||||
}
|
||||
|
||||
Assert.IsTrue(service2.Disposed);
|
||||
Assert.IsFalse(service1.Disposed);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ServiceRegisteredWithScopedReturnsNullWhenNoScope()
|
||||
{
|
||||
var serviceProvider = new ServiceCollection()
|
||||
.AddScoped(typeof(IFakeService), typeof(FakeService))
|
||||
.BuildServiceProvider();
|
||||
|
||||
|
||||
var service = serviceProvider.GetService(typeof(IFakeService));
|
||||
|
||||
Assert.IsNull(service);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ServiceRegisteredWithScopedThrowsExceptionWhenValidateScopesEnabledAndNoScope()
|
||||
{
|
||||
var serviceProvider = new ServiceCollection()
|
||||
.AddScoped(typeof(IFakeService), typeof(FakeService))
|
||||
.BuildServiceProvider(new ServiceProviderOptions{ValidateScopes = true});
|
||||
|
||||
|
||||
Assert.ThrowsException(typeof(InvalidOperationException),
|
||||
() => serviceProvider.GetService(typeof(IFakeService)));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void NoDuplicateServiceRegisteredWithScoped()
|
||||
{
|
||||
var serviceProvider = new ServiceCollection()
|
||||
.AddScoped(typeof(IFakeService), typeof(FakeService))
|
||||
.BuildServiceProvider();
|
||||
|
||||
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
var services = scope.ServiceProvider.GetServices(typeof(IFakeService));
|
||||
|
||||
Assert.AreEqual(1, services.Length);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IServiceProviderCreateScopeReturnsNewScope()
|
||||
{
|
||||
IServiceProvider serviceProvider = new ServiceCollection()
|
||||
.BuildServiceProvider();
|
||||
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
|
||||
Assert.IsNotNull(scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче