Scoped ServiceManager calls original ServiceManager when resolving singletons

This commit is contained in:
Alexandru Macocian
2021-06-03 13:03:29 +02:00
parent d13914224b
commit 1a012c5d49
5 changed files with 68 additions and 9 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2020 Macocian Alexandru Victor
Copyright (c) 2021 Macocian Alexandru Victor
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+45
View File
@@ -0,0 +1,45 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Slim.Tests.Models;
using System;
using System.Threading.Tasks;
namespace Slim.Tests
{
[TestClass]
public sealed class MemoryLeakTests
{
private const int ScopesCount = 10000;
[TestMethod]
public async Task CreateAndReleaseScopesDisposeManagerGCCollects()
{
var di = new ServiceManager();
di.RegisterSingleton<IIndependentService, IndependentService>();
di.RegisterScoped<IDependentService, DependentService>();
var independentService = di.GetService<IIndependentService>();
var dependentService = di.GetService<IDependentService>();
var weakRefs = new WeakReference[ScopesCount];
for (int i = 0; i < ScopesCount; i++)
{
var scopedDi = di.CreateScope();
weakRefs[i] = new WeakReference(scopedDi);
var scopedDependentService = scopedDi.GetService<IDependentService>();
scopedDependentService.Should().NotBeNull();
scopedDependentService.Should().NotBe(dependentService);
scopedDependentService.IndependentService.Should().NotBeNull();
scopedDependentService.IndependentService.Should().Be(independentService);
}
await Task.Delay(1500);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
foreach(var weakRef in weakRefs)
{
weakRef.IsAlive.Should().BeFalse();
}
}
}
}
+1 -1
View File
@@ -429,9 +429,9 @@ namespace Slim.Tests
{
var di = new ServiceManager();
di.RegisterSingleton<IIndependentService, IndependentService>();
var scopedDi = di.CreateScope();
var service = di.GetService<IIndependentService>();
var scopedDi = di.CreateScope();
var scopedService = scopedDi.GetService<IIndependentService>();
service.Should().Be(scopedService);
+18 -4
View File
@@ -673,7 +673,7 @@ namespace Slim
var dictionary = new Dictionary<Type, object>();
foreach (var kvp in this.Instances)
{
if (this.InterfaceMapping.First(s => s.Value.Item1 == kvp.Key).Value.Item2 is not Lifetime.Scoped)
if (this.InterfaceMapping.First(s => s.Value.Item1 == kvp.Key).Value.Item2 is Lifetime.Singleton)
{
dictionary.Add(kvp.Key, kvp.Value);
}
@@ -687,8 +687,22 @@ namespace Slim
var factories = new Dictionary<Type, Delegate>(this.Factories);
var exceptionHandlers = new Dictionary<Type, Delegate>(this.ExceptionHandlers);
var resolvers = new List<IDependencyResolver>(this.Resolvers);
var singletons = this.GetSingletons();
return new ServiceManager(interfaceMapping, singletons, factories, exceptionHandlers, resolvers);
/*
* For singletons, create a factory in scoped service manager that should call this manager
* to retrieve the singleton.
* This means that if the singleton has not yet been created in this service manager,
* the scoped service manager will not create a new one but instead ask the original service manager to
* create the singleton instead.
*/
foreach (var kvp in this.InterfaceMapping)
{
if (kvp.Value.Item2 is Lifetime.Singleton)
{
factories[kvp.Key] = new Func<IServiceProvider, object>((_) => this.PrepareAndGetService(kvp.Key));
}
}
return new ServiceManager(interfaceMapping, new Dictionary<Type, object>(), factories, exceptionHandlers, resolvers);
}
}
}
}
+3 -3
View File
@@ -8,10 +8,10 @@
<Company />
<Product></Product>
<Description>Service lifetime management and dependency injection framework.</Description>
<AssemblyVersion>1.4.2.0</AssemblyVersion>
<FileVersion>1.4.2.0</FileVersion>
<AssemblyVersion>1.4.3.0</AssemblyVersion>
<FileVersion>1.4.3.0</FileVersion>
<PackageProjectUrl>https://github.com/AlexMacocian/Slim</PackageProjectUrl>
<Version>1.4.2</Version>
<Version>1.4.3</Version>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>