Giter Site home page Giter Site logo

barsonax / singularity Goto Github PK

View Code? Open in Web Editor NEW
76.0 7.0 12.0 16.65 MB

A extremely fast ioc container for high performance applications

Home Page: https://barsonax.github.io/Singularity.Docs/

License: GNU Lesser General Public License v3.0

C# 98.80% PowerShell 0.40% HTML 0.56% CSS 0.20% JavaScript 0.03%
duality ioc ioc-container dependency-injection csharp di inversion-of-control inversionofcontrol ioc-framework singularity dependency-injection-container dotnet netcore netstandard di-container net

singularity's Introduction

Singularity

Discord NuGet Badge Build Status Azure DevOps tests (branch) Maintainability Rating Reliability Rating Security Rating coverage Beerpay

Features

  • Extreme performance, Singularity is one of the fastest if not the fastest dependency injection container out there. Don't believe me? Check out my benchmarks or if you want a second opinion check out the benchmarks that Daniel Palme made here.
  • Clean fluent API.
  • Source Link enabled
  • Generic wrappers:
    1. Func<T>
    2. Lazy<T>
    3. Expression<Func<T>>
    4. And any other generic wrapper you may have defined yourself.
  • Collection support:
    1. IEnumerable<T>
    2. IReadOnlyCollection<T>
    3. IReadOnlyList<T>
    4. T[]
    5. List<T>
    6. ICollection<T>
    7. IList<T>
    8. HashSet<T>
    9. ISet<T>
  • Supports open generics.
  • Supports resolving unregistered concrete types.
  • Supports decorators.
  • Supports method and property injection without forcing you to litter attributes all over your code base. All configuration is kept inside the container.
  • Supports dynamically picking the most suitable constructor based on the available types that can be resolved.
  • Auto dispose, this is off by default but can be turned on with With(DisposeBehavior)or adding the lifetimes you want to auto dispose to SingularitySettings.AutoDisposeLifetimes.
  • Custom finalizers with the WithFinalizer(Action<TInstance>) method.
  • Supports Transient, Singleton and Scope lifetimes.
  • Supports child containers.
  • Supports best fit constructor selection
  • Clear error messages and fail fast to point you in the right direction as fast as possible.

Getting started

Installation

Singularity can be installed through nuget. The packages that are available can be found in the nuget section

A simple example

Its easy to setup a container and request a instance:

var container = new Container(builder =>
{
    builder.Register<ITestService10, TestService10>();
});

var instance = container.GetInstance<ITestService10>();

However Singularity can do much more than this simple example. You can request the instance with different wrapper types such as Lazy<T>:

var lazyInstance = container.GetInstance<Lazy<ITestService10>>();

Or you can request the factory to create the instance:

var factory = container.GetInstance<Func<ITestService10>>();

You can even request the expression itself:

var instanceExpression = container.GetInstance<Expression<Func<ITestService10>>>();

Ofcourse its possible to combine these with for instance a collection type such as IEnumerable or IReadOnlyList:

var instanceExpressions = container.GetInstance<IReadOnlyList<Expression<Func<IPlugin>>>>(); //Returns all expressions for IPlugin registrations

Advanced scenarios such as open generics are also supported.

Documentation

More info about Singularity can be found on the documentation website which can be found here.

Other

Benchmarks

The code used in the benchmark can be found here

BenchmarkDotNet=v0.12.0, OS=Windows 10.0.18363
AMD Ryzen 9 3900X, 1 CPU, 24 logical and 12 physical cores
.NET Core SDK=3.1.200
  [Host]       : .NET Core 3.1.2 (CoreCLR 4.700.20.6602, CoreFX 4.700.20.6702), X64 RyuJIT
  LegacyJitX64 : .NET Framework 4.8 (4.8.4150.0), X64 RyuJIT
  RyuJitX64    : .NET Core 2.1.16 (CoreCLR 4.6.28516.03, CoreFX 4.6.28516.10), X64 RyuJIT

Platform=X64  IterationTime=1.0000 s

|    Method |          Job |       Jit |       Runtime |          Mean |       Error |      StdDev |  Gen 0 |  Gen 1 | Gen 2 | Allocated |
|---------- |------------- |---------- |-------------- |--------------:|------------:|------------:|-------:|-------:|------:|----------:|
| Singleton | LegacyJitX64 | LegacyJit |    .NET 4.7.2 |     10.539 ns |   0.1156 ns |   0.1082 ns |      - |      - |     - |         - |
| Transient | LegacyJitX64 | LegacyJit |    .NET 4.7.2 |     15.095 ns |   0.0562 ns |   0.0525 ns | 0.0145 |      - |     - |      24 B |
|  Combined | LegacyJitX64 | LegacyJit |    .NET 4.7.2 |     20.722 ns |   0.0707 ns |   0.0552 ns | 0.0338 |      - |     - |      56 B |
|   Complex | LegacyJitX64 | LegacyJit |    .NET 4.7.2 |     27.218 ns |   0.2894 ns |   0.2566 ns | 0.0580 |      - |     - |      96 B |
| Singleton |    RyuJitX64 |    RyuJit | .NET Core 2.1 |      9.569 ns |   0.0634 ns |   0.0593 ns |      - |      - |     - |         - |
| Transient |    RyuJitX64 |    RyuJit | .NET Core 2.1 |     14.755 ns |   0.1025 ns |   0.0959 ns | 0.0145 |      - |     - |      24 B |
|  Combined |    RyuJitX64 |    RyuJit | .NET Core 2.1 |     22.506 ns |   0.3656 ns |   0.3420 ns | 0.0337 |      - |     - |      56 B |
|   Complex |    RyuJitX64 |    RyuJit | .NET Core 2.1 |     26.756 ns |   0.3521 ns |   0.3294 ns | 0.0578 |      - |     - |      96 B |
| Singleton |    RyuJitX64 |    RyuJit | .NET Core 3.1 |     12.167 ns |   0.1213 ns |   0.1135 ns |      - |      - |     - |         - |
| Transient |    RyuJitX64 |    RyuJit | .NET Core 3.1 |     14.461 ns |   0.0698 ns |   0.0653 ns | 0.0029 |      - |     - |      24 B |
|  Combined |    RyuJitX64 |    RyuJit | .NET Core 3.1 |     20.933 ns |   0.2406 ns |   0.2251 ns | 0.0067 |      - |     - |      56 B |
|   Complex |    RyuJitX64 |    RyuJit | .NET Core 3.1 |     24.196 ns |   0.1059 ns |   0.0991 ns | 0.0115 |      - |     - |      96 B |

Nuget

Library Version
Singularity NuGet Badge
Singularity.Duality.core NuGet Badge
Singularity.Microsoft.DependencyInjection NuGet Badge
Singularity.AspNetCore.Hosting NuGet Badge
Singularity.AspNetCore.MVC NuGet Badge

Random info

GitHub repo size GitHub code size in bytes Lines of Code Duplicated Lines (%)

Build History

Donations

Paypal
paypal

Licensing

Licensed under LGPL.

singularity's People

Contributors

barsonax avatar jordansjones avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

singularity's Issues

Make ServiceBinding immutable

Currently some fields in ServiceBinding are mutable. This has some disadvantages:

  • It could cause hard to understand problems when reusing the same ServiceBinding instances across containers.
  • If ServiceBindings are immutable there is a possibility to turn this class into a struct for less memory usage (needs more research).

A possible solution would be to split the immutable and mutable part of ServicBinding into 2 different concepts.

Cannot add migration with EF

If Singularity is used as a dependency container for the project using UseServiceProviderFactory, dotnet ef migrations add throws an error:

Singularity.Exceptions.NoConstructorException: Type Microsoft.EntityFrameworkCore.DbContextOptions did not contain any public constructor.
   at Singularity.DefaultConstructorResolver.StaticSelectConstructor(Type type)
   at Singularity.ConstructorResolverCache.<StaticSelectConstructor>b__5_0(Type t)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Singularity.ConstructorResolverCache.StaticSelectConstructor(Type type)
   at Singularity.ConstructorResolverExtensions.ResolveConstructorExpression(IConstructorResolver resolver, Type type)
   at Singularity.Graph.Resolvers.ConcreteServiceBindingGenerator.Resolve(IResolverPipeline graph, Type type)+MoveNext()
   at System.Collections.Generic.LargeArrayBuilder`1.AddRange(IEnumerable`1 items)
   at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at Singularity.Graph.Resolvers.ResolverPipeline.TryGetDependency(Type type)
   at Singularity.Graph.Resolvers.ResolverPipeline.TryResolveAll(Type type)+MoveNext()
   at System.Linq.Enumerable.SelectEnumerableIterator`2.ToArray()
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at Singularity.Graph.Resolvers.CollectionServiceBindingGenerator.Resolve[TElement](IResolverPipeline graph, Type type)+MoveNext()
   at Singularity.Graph.Resolvers.CollectionServiceBindingGenerator.Resolve(IResolverPipeline graph, Type type)+MoveNext()
   at System.Collections.Generic.LargeArrayBuilder`1.AddRange(IEnumerable`1 items)
   at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at Singularity.Graph.Resolvers.ResolverPipeline.TryGetDependency(Type type)
   at Singularity.Graph.Resolvers.ResolverPipeline.GetDependency(Type type)
   at Singularity.Graph.Resolvers.ResolverPipeline.Resolve(Type type)
   at Singularity.Scoped.System.IServiceProvider.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetServices[T](IServiceProvider provider)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.FindContextTypes()
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.FindContextType(String name)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>c__DisplayClass0_0.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)

Workaround: remove call to UseServiceProviderFactory just for adding the migration

Split the IConstructorResolver interface

Is your feature request related to a problem? Please describe.
Currently both static and dynamic constructor selection is done with this interface. This makes the interface less clean than it could be and leads to some implementations sometimes only throwing a notimplemented exception because its not used.

Describe the solution you'd like
Split the interface in a dynamic and a static constructor selector interface. Also while changing this code try to reduce duplicate code for both clarity and performance. For instance constructor candidates are searched for twice then using the dynamic constructor selector currently. A option/result like type encapsulating the result of the static constructor selection could solve this.

Construction of service with unregistered interface as dependency fails silently when requested in a collection

Describe the bug

Resolving IReadOnlyCollection<ICommon> (or other collection types) with the configuration shown below results in a silent failure to construct Dependency2 as the constructor parameter type ITransitiveDependency is not registered.

This behavior leads to hard to diagnose misconfigurations.

To Reproduce

Example code hosted here.

public static class Program
{
    public static void Main()
    {
        var container = new Container(builder => {
            builder.Register<ICommon, Dependency1>();
            builder.Register<ICommon, Dependency2>();
        });

        var instances = container.GetInstance<IReadOnlyCollection<ICommon>>();
        // Contains only Dependency1
        // instances.Count == 1

        var instance = container.GetInstance<Dependency2>();
        // Fails as expected:
        // Singularity.Exceptions.DependencyResolveException: Failed to resolve dependency SingularityEnumerable.Dependency2
    }
}

public interface ICommon { }

public class Dependency1 : ICommon { }

public class Dependency2 : ICommon
{
    public Dependency2(ITransitiveDependency unregistered) { }
}

public interface ITransitiveDependency { }

Expected behavior
The call to container.GetInstance<IReadOnlyCollection<ICommon>>() should fail with the same exception as the call to container.GetInstance<Dependency2>().

How to use in ASP.NET Core?

It's not very clear from the documentation how to setup Singularity as the DI Container in an ASP.NET Core application.

This is my code in Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
   services.AddControllers()
           .AddControllersAsServices()
           .SetCompatibilityVersion(CompatibilityVersion.Version_3_0);

   var container = new Container(cb =>
   {
      cb.RegisterServiceProvider();

      // My Singularity registrations
   });
}

Getting the generic ASP.NET Core exception instead:

InvalidOperationException: Unable to resolve service for type '<MyService>' while attempting to activate '<MyController>'.

I'm using ASP.NET Core 3.0.

Stack overflow in worker service if ExecuteAsync throws an exception

To reproduce:

Create a new worker service - dotnet new worker
Install Singularity.Microsoft.DependencyInjection (v0.17.1)
Worker.cs -

using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;

namespace test
{
    public class Worker : BackgroundService
    {

        public Worker(IServiceProvider Provider)
        {
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            throw new Exception("a");
        }
    }
}

Program.cs -

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Singularity;

namespace test
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseServiceProviderFactory(new SingularityServiceProviderFactory(SingularitySettings.Microsoft))
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddHostedService<Worker>();
                });
    }
}

Workaround - don't throw exceptions in ExecuteAsync

Fix the wrong build status being reported

Describe the bug
We currently use multi stage builds in combination with manual approvals. This is so that its easy to deploy from any branch. However Azure Devops is reporting the build as waiting until a approval or rejection is given. This is not desirable as the approval in this workflow is optional. Azure Devops does not seem to support this though..

I don't think this is really fixable by changing the build so we have to wait till microsoft fixes this:
https://github.com/MicrosoftDocs/vsts-docs/issues/5700

Double initialization when resolving strongly typed singleton registrations using concrete type

Describe the bug
One feature of Singularity is "Supports resolving unregistered concrete types". I may misunderstand how to use it, but I think those two registrations should be equivalent (where WritingSingleton implements ISingleton):

builder.Register<ISingleton, WritingSingleton>(c => c
    .With(Lifetimes.PerContainer));

builder.Register(typeof(WritingSingleton), c => c
    .As(typeof(ISingleton))
    .With(Lifetimes.PerContainer));

However, I find that in the first, strongly typed registration the singleton will be initialized once when resolving ISingleton, and another time when resolving WritingSingleton.

To Reproduce

using System;

namespace Singularity.DoubleInit
{
    public interface ISingleton { }

    public class WritingSingleton : ISingleton
    {
        public WritingSingleton() => Console.WriteLine("Initialized");
    }

    class Program
    {
        static void Main(string[] args)
        {
            var container = new Container(builder =>
            {
                // This registration does result in two initializations of WritingSingleton
                //builder.Register<ISingleton, WritingSingleton>(c => c.With(Lifetimes.PerContainer));

                // This registration does result in one initialization of WritingSingleton
                builder.Register(typeof(WritingSingleton), c => c.As(typeof(ISingleton)).With(Lifetimes.PerContainer));
            });

            var instance1 = container.GetInstance<ISingleton>();
            var instance2 = container.GetInstance<ISingleton>();
            var instance3 = container.GetInstance<WritingSingleton>();
        }
    }
}

Expected behavior
Both registrations should result in Initialized being written to the console only once.

GetInstance<T>() should return non-nullable T

Describe the bug
Method GetInstance<T?>() may return nullable type but it never be happen because exception will be thrown if unable to resolve it.

To Reproduce
Screen-573

Expected behavior
Method GetInstance<T> should accept T as generic parameter and return T type without nullable.
MethodGetInstanceOrDefault<T?>may accept T? as generic parameter and return T? with nullable type (return null if it's not resolved).

Support injection via arrays

Currently, it can inject into IEnumerable, but it can't in an array. So if you got, for example, an constructor like;

public CookieBaker(Ingredient[] ingredients)

It won't work. I wanted to implement my own IServiceBindingGenerator based on the EnumerableServiceBindingGenerator, but stuff like ExpressionGenerator.ScopeParameter and BindingMetadata.GeneratedInstance are internal so it's not feasible within my own project.

Final goal is to use this to implement a dynamic decorator using using the Castle DynamicProxy, which generates a new constructor which expects IInterceptor[].

I've worked around it by doing;

var container = new Container(b =>
{
	var interceptors = new IInterceptor[] { new TestInterceptor() };
	b.Register<IInterceptor[]>(c => c.Inject(() => interceptors));

	b.Register<ITestInterface, TestClass>();

        var proxyType = ProxyBuilder.CreateInterfaceProxyTypeWithTargetInterface(typeof(ITestInterface), Array.Empty<Type>(), ProxyGenerationOptions.Default);
	b.Decorate(typeof(ITestInterface), proxyType);
});

It works in my use case because I don't need IoC in my interceptor, but I might imagine there are more elegant ways to solve this.

No way to remove type from ConstructorResolverCache

Describe the bug
Some types used with Singularity may be a part of AssemblyLoadContext which holds a set of assemblies which may be then unloaded together with AssemblyLoadContext unless something holds a reference to the assembly or one of its types. https://github.com/dotnet/docs/blob/d2ca43e5e87ca76ad737e586014b82eb7a4c3fc3/docs/standard/assembly/unloadability.md#troubleshoot-unloadability-issues

If you use Singularity to construct a class of an unloadable assembly, Singularity (I assume) puts the compiled expression which does the heavy lifting into the global cache. However, after unloading, the type practically doesn't exist now.

There should be a way to remove a type from ConstructorResolverCache.

To Reproduce
Steps to reproduce the behavior:
Run Release build:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Singularity;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;

namespace test
{
    class UnloadableLoadContext : AssemblyLoadContext
    {
        public UnloadableLoadContext() : base(true) { }
        protected override Assembly Load(AssemblyName assemblyName) => null;
    }

    class Program
    {
        static readonly CSharpParseOptions parseOpts = new CSharpParseOptions(
            kind: SourceCodeKind.Regular,
            languageVersion: LanguageVersion.Latest);
        static readonly CSharpCompilationOptions options = new CSharpCompilationOptions(
            OutputKind.DynamicallyLinkedLibrary,
            optimizationLevel: OptimizationLevel.Release,
            allowUnsafe: false);

        static void Main(string[] args)
        {
            var references = new List<MetadataReference>() {
                MetadataReference.CreateFromFile(typeof(Binder).Assembly.Location),
                MetadataReference.CreateFromFile(AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.FullName.Substring(0, x.FullName.IndexOf(',')) == "netstandard").Location),
                MetadataReference.CreateFromFile(AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.FullName.Substring(0, x.FullName.IndexOf(',')) == "System.Console").Location),
                MetadataReference.CreateFromFile(AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.FullName.Substring(0, x.FullName.IndexOf(',')) == "System.Runtime").Location)
            };

            var csTree = CSharpSyntaxTree.ParseText("class A { public void B() { System.Console.WriteLine(\"hello\"); } }", parseOpts);
            var asmName = Guid.NewGuid().ToString();
            Compilation compilation = CSharpCompilation.Create(asmName, options: options, references: references).AddSyntaxTrees(csTree);

            using (var ms = new MemoryStream()) {
                var emitResult = compilation.Emit(ms);
                ms.Seek(0, SeekOrigin.Begin);

                var alc = new UnloadableLoadContext();
                var alcWeakRef = new WeakReference(alc);
                var asm = alc.LoadFromStream(ms);

                var mthd = asm.GetType("A").GetMethod("B");

                var inst = Activator.CreateInstance(asm.GetType("A"));

                mthd.Invoke(inst, null);
                alc.Unload();

                for (int i = 0;alcWeakRef.IsAlive && (i < 10);i++) {
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                }

                if (alcWeakRef.IsAlive)
                    Console.WriteLine("[no singularity] noooooooo");
                else
                    Console.WriteLine("[no singularity] all good");
            }

            compilation = CSharpCompilation.Create(asmName, options: options, references: references).AddSyntaxTrees(csTree);

            using (var ms = new MemoryStream()) {
                var emitResult = compilation.Emit(ms);
                ms.Seek(0, SeekOrigin.Begin);

                var alc = new UnloadableLoadContext();
                var alcWeakRef = new WeakReference(alc);
                var asm = alc.LoadFromStream(ms);

                var mthd = asm.GetType("A").GetMethod("B");

                using (var container = new Container()) {
                    using (var scope = container.BeginScope()) {
                        var inst = container.GetInstance(asm.GetType("A"));

                        mthd.Invoke(inst, null);
                    }
                    alc.Unload();
                }

                for (int i = 0;alcWeakRef.IsAlive && (i < 10);i++) {
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                }

                if (alcWeakRef.IsAlive)
                    Console.WriteLine("[with singularity] noooooooo");
                else
                    Console.WriteLine("[with singularity] all good");
            }
        }
    }
}

Expected behavior

hello
[no singularity] all good
hello
[with singularity] all good

Current behavior

hello
[no singularity] all good
hello
[with singularity] noooooooo

Consider using struct based implementation for the immutable dictionary

A struct based implementation might be a nanosecond or 2 faster than the current implementation. The same change was done with the MinHeap in Pathfindax and it let to substantial performance gains.

EDIT: Some testing revealed that the lookup times are extremely fast already (few nanoseconds). However adding items is quite slow due to alot of allocations. Adding might be faster when using a struct based implementation due to less memory being allocated.

Add a smart constructor selector

Add a smart constructor selector that takes into account what can be resolved and picks the constructor with the most parameters that still can be resolved.

However current selectors select the contructors at register time where its not yet known what can be resolved. This must keep working as it allows for fail fast behavior.

So Singularity has to support both static constuctor selection (registration time) and late bound contructor selection (when a instance is being resolved).

Question about resolving

I have a project in which I used Singularity, and it has a very simple setup -|
builder.Register<Configuration>(conf => conf.With(Lifetimes.PerContainer)); builder.Register<IConnectionFactory, ConnectionFactory>();
which is resolved in a final class -
public Service(DatabaseHandler dbHandler, RemoteDatabaseHandler remoteDbHandler, Server server, Publisher publisher, Router router, ServiceMessagePump messagePump, Configuration configuration) and everything works.
Looking at the documentation, I though that this should also work -
public Service(Func<string, DatabaseHandler> dbHandlerFactory, RemoteDatabaseHandler remoteDbHandler, Server server, Publisher publisher, Router router, ServiceMessagePump messagePump, Configuration configuration) but this does not work, because Singularity is not able to resolve DatabaseHandler. This is it's constructor which works
public DatabaseHandler(IConnectionFactory factory) - and I though that if I change it like
public DatabaseHandler(IConnectionFactory factory, string connectionType) I could resolve it like Func<string, DatabaseHandler> and then get an instance like DatabaseHandler = dbHandlerFactory("some string...").
Should this work or are my assumptions wrong?
Thanks.

No way to remove type from RegistrationStore and _getInstanceCache

Describe the bug
Related: #133

Hey again, if the reproduction in issue #133 is used with a dependency from main module in compiled module, there is two more places where type is stored but never removed from the system even if the scope is disposed.

To Reproduce

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Singularity;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;

namespace test
{
    class UnloadableLoadContext : AssemblyLoadContext
    {
        public UnloadableLoadContext() : base(true) { }
        protected override Assembly Load(AssemblyName assemblyName) => null;
    }

    public class Aaa
    {
        public string GetHello() => "hello :)";
    }

    class Program
    {
        static readonly CSharpParseOptions parseOpts = new CSharpParseOptions(
            kind: SourceCodeKind.Regular,
            languageVersion: LanguageVersion.Latest);
        static readonly CSharpCompilationOptions options = new CSharpCompilationOptions(
            OutputKind.DynamicallyLinkedLibrary,
            optimizationLevel: OptimizationLevel.Release,
            allowUnsafe: false);

        static Container Container;

        static void Main(string[] args)
        {
            Container = new Container(builder => {
                builder.Register<Aaa>();
            });

            var references = new List<MetadataReference>() {
                MetadataReference.CreateFromFile(typeof(Binder).Assembly.Location),
                MetadataReference.CreateFromFile(AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.FullName.Substring(0, x.FullName.IndexOf(',')) == "netstandard").Location),
                MetadataReference.CreateFromFile(AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.FullName.Substring(0, x.FullName.IndexOf(',')) == "System.Console").Location),
                MetadataReference.CreateFromFile(AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.FullName.Substring(0, x.FullName.IndexOf(',')) == "System.Runtime").Location),
                MetadataReference.CreateFromFile(typeof(Aaa).Assembly.Location)
            };

            var csTree = CSharpSyntaxTree.ParseText("using test; class A { private readonly Aaa AAA; public A(Aaa AAA) { this.AAA = AAA; } public void B() { System.Console.WriteLine(AAA.GetHello()); } }", parseOpts);
            var asmName = Guid.NewGuid().ToString();
            var compilation = CSharpCompilation.Create(asmName, options: options, references: references).AddSyntaxTrees(csTree);

            using (var ms = new MemoryStream()) {
                var emitResult = compilation.Emit(ms);
                ms.Seek(0, SeekOrigin.Begin);

                var alc = new UnloadableLoadContext();
                var alcWeakRef = new WeakReference(alc);
                var asm = alc.LoadFromStream(ms);

                var type = asm.GetType("A");
                var mthd = type.GetMethod("B");

                using (var scope = Container.BeginScope()) {
                    var inst = scope.GetInstance(type);

                    mthd.Invoke(inst, null);
                }
                alc.Unload();

                for (int i = 0;alcWeakRef.IsAlive && (i < 10);i++) {
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                }

                /*
                For successful free:
                Container.Registrations.RemoveFromRegistrations(type);
                ^^^ public void RemoveFromRegistrations(Type type)
                {
                    Registrations.Remove(type);
                }
                and remove Container.cs @ 123
                */

                if (alcWeakRef.IsAlive)
                    Console.WriteLine("[with singularity] noooooooo");
                else
                    Console.WriteLine("[with singularity] all good");
            }
        }
    }
}

(master @ 44dfa61)

Expected behavior
A way to remove the used type from RegistrationStore and _getInstanceCache in Container or any other solution

Screenshots
call stack for RegistrationStore devenv_9tFY0eLpGv

Null exception error

Sorry I don't speak English well

Register services by Singularity error viz video https://youtu.be/-hcqYX_ZwyM

by Microsoft OK and by LightInject OK

Repository is https://github.com/Olbrasoft/Blog

Singularity.Container.GetInstance(Type type, Scoped scope)
Singularity.Scoped.System.IServiceProvider.GetService(Type serviceType)
Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService(IServiceProvider provider)
Microsoft.EntityFrameworkCore.Internal.ServiceProviderCache+<>c__DisplayClass4_0.g__BuildServiceProvider|3()
Microsoft.EntityFrameworkCore.Internal.ServiceProviderCache+<>c__DisplayClass4_0.b__2(long k)
System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue>.GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
Microsoft.EntityFrameworkCore.Internal.ServiceProviderCache.GetOrAdd(IDbContextOptions options, bool providerRequired)
Microsoft.EntityFrameworkCore.DbContext..ctor(DbContextOptions options)
Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserContext<TUser, TKey, TUserClaim, TUserLogin, TUserToken>..ctor(DbContextOptions options)
Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TRoleClaim, TUserToken>..ctor(DbContextOptions options)
Olbrasoft.Blog.Data.EntityFrameworkCore.BlogDbContext..ctor(DbContextOptions options) in BlogDbContext.cs
+
public BlogDbContext(DbContextOptions options) : base(options)
(Closure<Func<Scoped, DbContextOptions>> , Scoped )
Singularity.Scoped.GetOrAddScopedInstance(Func<Scoped, T> factory, Type key)
(Closure<Func<Scoped, BlogDbContext>, Func<Scoped, IdentityErrorDescriber>> , Scoped )
Singularity.Scoped.GetOrAddScopedInstance(Func<Scoped, T> factory, Type key)
(Closure<Func<Scoped, UserStore<BlogUser, BlogRole, BlogDbContext, int, BlogUserClaim, BlogUserToRole, BlogUserLogin, BlogUserToken, BlogRoleClaim>>, OptionsManager, Func<Scoped, PasswordHasher>, Func<Scoped, IUserValidator>[], Func<Scoped, IPasswordValidator>[], Func<Scoped, UpperInvariantLookupNormalizer>, Func<Scoped, IdentityErrorDescriber>, Scoped, Logger<UserManager>> , Scoped )
Singularity.Scoped.GetOrAddScopedInstance(Func<Scoped, T> factory, Type key)
(Closure<Func<Scoped, UserManager>, HttpContextAccessor, Func<Scoped, UserClaimsPrincipalFactory<BlogUser, BlogRole>>, OptionsManager, Logger<SignInManager>, AuthenticationSchemeProvider, Func<Scoped, DefaultUserConfirmation>> , Scoped )
Singularity.Scoped.GetOrAddScopedInstance(Func<Scoped, T> factory, Type key)
(Closure<OptionsManager, Func<Scoped, SignInManager>, SystemClock, LoggerFactory> , Scoped )
Singularity.Scoped.GetOrAddScopedInstance(Func<Scoped, T> factory, Type key)
Singularity.Container.GetInstance(Type type, Scoped scope)
Singularity.Scoped.System.IServiceProvider.GetService(Type serviceType)
Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider)
Microsoft.AspNetCore.Identity.SecurityStampValidator.ValidateAsync(CookieValidatePrincipalContext context)
Microsoft.AspNetCore.Identity.SecurityStampValidator.ValidatePrincipalAsync(CookieValidatePrincipalContext context)
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationEvents.ValidatePrincipal(CookieValidatePrincipalContext context)
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler.HandleAuthenticateAsync()
Microsoft.AspNetCore.Authentication.AuthenticationHandler.AuthenticateAsync()
Microsoft.AspNetCore.Authentication.AuthenticationService.AuthenticateAsync(HttpContext context, string scheme)
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.MigrationsEndPointMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.Invoke(HttpContext httpContext)
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.Invoke(HttpContext httpContext)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Add logging

Add logging functionality that will show useful info such as what instances are resolved.

Logging should be optional and not add any performance hit in the happy path.

Adding more compile time safety to the StronglyTypedServiceConfigurator As method

@BrunoZell raised a issue with the registration api #138:

Regarding the generic constraint, check again. The ASP.NET Core's default dependency injection does use generic constraints, which are where TImplementation : class, TService (source). This definitely helps preventing rookie mistakes.

Currently the signature of the As method in StronglyTypedServiceConfigurator is this:

public StronglyTypedServiceConfigurator<TDependency, TInstance> As<TService>()
{
   // more code
}

This makes it impossible to add a generic constraint as its not possible to write where TInstance : TService. This means we have to rely on runtime checks to verify if the service type is correct. Consider changing the registration api to make it possible to move these checks to compile time.

Make method injection less invasive

Currently method injection requires the user to put Inject attributes on the methods. This means a dependency on the container has to be added. It would be better if the user could tell the binding config or container directly which methods to inject.

Come up with a better name

Is your feature request related to a problem? Please describe.
Singularity as a name is currently used by some other projects. It also does not have a direct link to dependency injection

Describe the solution you'd like
A better unique name which also describes better what Singularity is about

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.