Coder Social home page Coder Social logo

devteam / pure.di Goto Github PK

View Code? Open in Web Editor NEW
487.0 6.0 22.0 103.66 MB

Pure DI for .NET

License: MIT License

C# 98.87% Batchfile 0.04% Shell 0.01% Kotlin 0.01% HTML 1.08%
dependency-injection ioc solid inversion-of-control di csharp-sourcegenerator dotnet injection-container injection-framework pure

pure.di's People

Contributors

misinformeddna avatar nikolaypianikov avatar serg046 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  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

pure.di's Issues

Generic type composition root

For example:

interface IDependency;

class Dependency<T> : IDependency<T>;

interface IService<T>;

class Service<T>(IDependency<T> dependency) : IService<T>;

DI.Setup(nameof(Composition))
  .Bind<IDependency<TT>>().To<Dependency<TT>>()
  .Bind<IService<TT>>().To<Service<TT>>()
  .Root<IService<TT>>("MyRoot");

var composition = new Composition();
var root = composition.MyRoot<int>();

Using external values when resolving

Hi

While trying this out I ran into an issue that I've been unable to solve.

Most of our services needs to know the identity of the current user.
In our current DI setup we get this from ambient data when resolving (HttpContext.Current to be specific). I've never liked that much, but I'm not sure there's any way around that in old Asp.Net.

I've been trying to solve that issue with Pure.DI and have come up with 4(5) solutions, but none are to my satisfaction and one doesn't compile. So I'm writing now in the hope of some guidance or ideally pointers to some obvious solution that I've missed.

I'll detail the five solutions here:

Two of the solutions are partially implemented in my fork of this project, but neither have been finished. There's quite a lot of work to do yet and since I'm not really interested in maintaining my own fork, I'm not going to continue the work unless there is some chance of getting the changes merged.

The first api change introduces IConfiguration BindAtResolve<T>(); to IConfiguration. The idea is that T will be added as an argument to the Resolve methods generated and those arguments being used to resolve types of T. I've yet to figure out if adding a new property for these in ResolverMetadata or adding them to ResolverMetadata.Bindings and adding a new binder or something else is the right way to do this.
This change is backwards-compatible and should work well with DependsOn.

The other api change adds generic argument TContextArgs to Setup and adds TContextArgs Args {get;} to IContext. This change requires IConfiguration, IBinding etc. to become generic on TContextArgs. An non-generic overload Setup() => Setup<Unit>() is provided to avoid breaking changes.
The generated Resolve methods will take TContextArgs as first argument unless it is Unit and that argument will then be available in the ctx argument in To<> methods.
This change will not play nice with DependsOn unless TContextArg is the same for all dependencies.
When I started work on this I hadn't understood that ctx mostly (only) acts as a placeholder and isn't actually available at runtime, so it's possible that it's never going to work.

Three of the solutions are using the project as-is and are reproduced in the following:

using System;
using Pure.DI;

namespace AppStart.Pure_DI;


public struct Identity {
    public int UserId;
    // More fields
}

public interface IIdentityService {
    Identity Identity { get; }
}

public class IdentityService: IIdentityService {
    public IdentityService(Identity identity) {
        Identity=identity;
    }

    public Identity Identity { get; }
}


internal class SomeService {
    public SomeService(IIdentityService identityService) {
        IdentityService=identityService;
    }

    public IIdentityService IdentityService { get; }
}

public static class Test {
    public static void Run() {
        Run(new Identity { UserId = 42 });
    }

    public static void Run(Identity ident) {
        // This fails at compile-time. See details in bindings
        SomeService s1 = ComposerX.Resolve<Func<Identity, WithIdentityResolver>>()(ident).Resolve<SomeService>();


        // This fails at run-time. See details in bindings
        SomeService s2 = ComposerX.Resolve<Func<Identity, GenericCompositionRootWithIdentity<SomeService>>>()(ident).Value;

        SomeService s3 = ComposerX.Resolve<Func<Identity, SpecializedCompositionRootWithIdentity>>()(ident).SomeService;
    }
}


class WithIdentityResolver {
    public WithIdentityResolver(IContext resolverContext) {
        ResolverContext=resolverContext;
    }

    public IContext ResolverContext { get; }

    public T Resolve<T>() {
        return ResolverContext.Resolve<T>();
    }
}


internal class GenericCompositionRootWithIdentity<T> {
    public GenericCompositionRootWithIdentity(T value) {
        Value=value;
    }

    public T Value { get; }
}


internal class SpecializedCompositionRootWithIdentity {
    public SpecializedCompositionRootWithIdentity(SomeService someService) {
        SomeService=someService;
    }

    public SomeService SomeService { get; }
}


static partial class ComposerX {

    class CachedService<TService> {
        public TService Service { get; set; }
    }

    static ComposerX() =>
        // out=C:\tmp
        // verbosity=Diagnostic
        DI.Setup()
            .Default(Lifetime.PerResolve)
            .Bind<CachedService<TT>>().To<CachedService<TT>>()
            .Bind<Func<Identity, IdentityService>>().To(ctx => new Func<Identity, IdentityService>(ident =>
                ctx.Resolve<CachedService<IdentityService>>().Service = new(ident)
            ))
            .Bind<IIdentityService>().To(ctx => ctx.Resolve<CachedService<IdentityService>>().Service)
            .Bind<SomeService>().To<SomeService>()

            // Doesn't work because Funcs with generic return values can't be found
            // Cannot resolve an instance System.Func`2[AppStart.Pure_DI.Identity,AppStart.Pure_DI.GenericCompositionRootWithIdentity`1[AppStart.Pure_DI.SomeService]], consider adding it to the DI setup.
            .Bind<Func<Identity, GenericCompositionRootWithIdentity<TT>>>().To(ctx =>
                new Func<Identity, GenericCompositionRootWithIdentity<TT>>(ident => {
                    // This Func resolve ensures that IdentityService is cached for the current resolve
                    ctx.Resolve<Func<Identity, IdentityService>>()(ident);
                    return new(ctx.Resolve<TT>());
                }))

            // Doesn't work because resolves on ctx are rebound and ctx itself is not available in the rewritten Func
            //  error CS0103: The name 'ctx' does not exist in the current context
            //[System.Runtime.CompilerServices.MethodImplAttribute((System.Runtime.CompilerServices.MethodImplOptions)768)]private static System.Func<AppStart.Pure_DI.Identity, AppStart.Pure_DI.WithIdentityResolver> GetPerResolveSystemFuncAppStartPure_DIIdentityAppStartPure_DIWithIdentityResolver(){if( _perResolveSystemFuncAppStartPure_DIIdentityAppStartPure_DIWithIdentityResolver==default(System.Func<AppStart.Pure_DI.Identity, AppStart.Pure_DI.WithIdentityResolver>)){ _perResolveSystemFuncAppStartPure_DIIdentityAppStartPure_DIWithIdentityResolver=                new Func<Identity,WithIdentityResolver>(ident => {
            //( GetPerResolveSystemFuncAppStartPure_DIIdentityAppStartPure_DIIdentityService())(ident);
            //                    return new(ctx);
            //            });
            .Bind<Func<Identity, WithIdentityResolver>>().To(ctx =>
                new Func<Identity, WithIdentityResolver>(ident => {
                    // This Func resolve ensures that IdentityService is cached for the current resolve
                    ctx.Resolve<Func<Identity, IdentityService>>()(ident);
                    return new WithIdentityResolver(ctx);
                }))

            // Does work but will cause binding bloat
            .Bind<Func<Identity, SpecializedCompositionRootWithIdentity>>().To(ctx =>
                new Func<Identity, SpecializedCompositionRootWithIdentity>(ident => {
                    // This Func resolve ensures that IdentityService is cached for the current resolve
                    ctx.Resolve<Func<Identity, IdentityService>>()(ident);
                    return new(ctx.Resolve<SomeService>());
                }))


            ;
}

I'm not really sure where to go from here, so any helpful would be appriciated.

Cheers,
John

Singleton Instance Not Shared for Multiple Interfaces

I expect with the registration below to share the same instance of ServiceHost when resolving IServiceHost and IApplicationLifecycleManager. However I can see that two instances are created.

class ServiceHost : IServiceHost, IApplicationLifecycleManager
{
}
.Root<ServiceApplication>("Root")
            .Bind<Func<bool, ServiceHostEnvironment>>().To<Func<bool, ServiceHostEnvironment>>(ctx => (bool flag) => 
            {
                ctx.Inject(out Func<IServiceHost> serviceHostFactory);
                return new ServiceHostEnvironment(serviceHostFactory);
            })
            .Bind<IService>().To(ctx =>
            {
                ctx.Inject(out IApplicationLifecycleManager applicationLifecycleManager);
                return new StagingService(applicationLifecycleManager);
            })
            .Bind<Services.ServiceHost>().As(Singleton).To<Services.ServiceHost>()
            .Bind<IServiceHost>().To<Services.ServiceHost>()
            .Bind<IApplicationLifecycleManager>().To<Services.ServiceHost>()
            ;

If I change the last two lines to

            .Bind<IServiceHost>().To(ctx => 
            {
                ctx.Inject<Services.ServiceHost>(out var instance);
                return instance;
            })
            .Bind<IApplicationLifecycleManager>().To(ctx =>
            {
                ctx.Inject<Services.ServiceHost>(out var instance);
                return instance;
            })
            ;

It seems to be working as expected

Enumerable injection issue

I am injecting IEnumerable< IService >. My expectation is to receive IEnumerable with two already created intances but I got instead of instances, factories. So basically the behavior is like I am injecting IEnumerable< Func < IService >>.

Is this by design?

    .Bind<IService>(1).To(ctx =>
    {
        return new StagingService();
    })
    .Bind<IService>(2).To(ctx =>
    {

        return new RegistrationService();
    })

    class ServiceHost
    {
        ServiceHost(IEnumerable<IService> services)
        {
            foreach (IService service in services) // each iteration will create new instance of IService
            {
 
            }

            foreach (IService service in services) // iterating again, does not reuse instances but create a new ones
            {

            }
        }
    }

Add option to not generate `IServiceCollection` method

One use case is for MAUI app, they use MEDI but I only use Pure.DI, but Pure.DI still add the method for IServiceCollection even though it's not needed at least in my use case because the app has reference to MEDI

Incorrect source generated

I have this registration code

namespace MobiSystems.ServiceHost
{
    internal partial class Composition
    {
        // IMPORTANT!!! This class should not contain any other methods, properties, fields and etc. other than Setup method

        private void Setup() => DI.Setup(nameof(Composition))
            .Root<ServiceApplication>("Root")
            .Bind<IEnumerable<TT>>().To(ctx =>
            {
                ctx.Inject(out IReadOnlyCollection<TT> items);
                return items;
            })
            .Bind<IProcessRegistry>().As(Singleton).To(ctx => new ProcessRegistry(ProcessRegistryCommonNameProvider.GetName(ServiceApplication.GetProductFamilyName()), (int)ProductType.ManagementService))
            .Bind<Func<bool, ServiceHostEnvironment>>().To<Func<bool, ServiceHostEnvironment>>(ctx => (bool registerForRestart) =>
            {
                ctx.Inject(out Func<ILogger2> loggerFactory);
                ctx.Inject(out Func<IServiceHost> serviceHostFactory);
                return new ServiceHostEnvironment(loggerFactory, serviceHostFactory, registerForRestart);
            })
            .Bind<Microsoft.Extensions.Configuration.IConfiguration>().As(Singleton).To(ctx => 
            {
                AppConfiguration configuration = new AppConfiguration();
                configuration.Init("MobiSystemsPackaged\\Configuration", "MobiSystems.ServiceHost.appsettings.json");
                return configuration;
            })
            .Bind<ITelemetry>().As(Singleton).To(ctx => 
            {
                //SentryIOTelemetry requires TelemetryRuntime to be initialized first
                ctx.Inject(out TelemetryRuntime telemetryRuntime);
                return new SentryIOTelemetry();
            })
            .Bind().As(Singleton).To<TelemetryRuntime>(ctx => 
            {
                ctx.Inject(out IDeploymentContext deploymentContext);
                ctx.Inject(out Microsoft.Extensions.Configuration.IConfiguration configuration);

                TelemetryRuntimeParameters parameters = new TelemetryRuntimeParameters(
                    "MobiSystems.ServiceHost",
                    configuration["TelemetryHost"],
                    configuration["BuildType"],
                    deploymentContext.DeploymentType.GetDeploymentTypeName(),
                    (_) => true);

                return new TelemetryRuntime(parameters);
            })
            .Bind<IDeploymentContext>().As(Singleton).To<DeploymentContext>()
            .Bind<IService>(1).To<PackageUpdaterService>()
            .Bind<ILogger2>().As(Singleton).To(ctx =>
            {
                Logging.Sinks.File fileSink = new("c:\\Temp\\service-host.txt", new DefaultLogsCleaner());

                return new LoggerConfiguration()
                    .WriteTo(fileSink)
                    .CreateLogger();
            })
            .Bind().As(Singleton).To<Services.ServiceHost>()
            ;
    }
}

I am getting null reference exception at

                TelemetryRuntimeParameters localParametersM05D20di9= new TelemetryRuntimeParameters(
                    "MobiSystems.ServiceHost",
localConfigurationM05D20di8["TelemetryHost"],
localConfigurationM05D20di8["BuildType"],
localDeploymentContextM05D20di7.DeploymentType.GetDeploymentTypeName(),
                    (_) => true);

It says localConfigurationM05D20di8 is null.

Seems to me that generated code is incorrect. If you look at line 179
EnsureExistenceOf_singletonSentryIOTelemetryM05D20di36();
there is nothing to instantiate AppConfiguration instance localConfigurationM05D20di8.

// <auto-generated/>
// by Pure.DI 2.1.19+792162136ec813fa5eadf6acf1b4563282a7abe6

using Microsoft.Extensions.Configuration;
using MobiSystems.Logging;
using MobiSystems.Logging.Sinks;
using MobiSystems.Packages;
using MobiSystems.ServiceHost;
using MobiSystems.Services;
using MobiSystems.Telemetry.SentryIO;
using MobiSystems.Windows;
using MobiSystems.Windows.Threading;
using OfficeSuite;
using Pure.DI;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;

namespace MobiSystems.ServiceHost
{
	/// <summary>
	/// <para>
	/// <b>Composition roots</b><br/>
	/// <list type="table">
	/// <listheader>
	/// <term>Root</term>
	/// <description>Description</description>
	/// </listheader>
	/// <item>
	/// <term>
	/// <see cref="MobiSystems.ServiceHost.ServiceApplication"/> <see cref="Root"/><br/>or using <see cref="Resolve{T}()"/> method: <c>Resolve&lt;MobiSystems.ServiceHost.ServiceApplication&gt;()</c>
	/// </term>
	/// <description>
	/// Provides a composition root of type <see cref="MobiSystems.ServiceHost.ServiceApplication"/>.
	/// </description>
	/// </item>
	/// </list>
	/// </para>
	/// </summary>
	/// <example>
	/// This shows how to get an instance of type <see cref="MobiSystems.ServiceHost.ServiceApplication"/> using the composition root <see cref="Root"/>:
	/// <code>
	/// using var composition = new Composition();
	/// var instance = composition.Root;
	/// </code>
	/// </example>
	/// This class was created by <a href="https://github.com/DevTeam/Pure.DI">Pure.DI</a> source code generator.
	/// <seealso cref="Pure.DI.DI.Setup"/>
	/// <seealso cref="Pure.DI.IConfiguration.Bind(object[])"/>
	/// <seealso cref="Pure.DI.IConfiguration.Bind{T}(object[])"/>
	[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
	partial class Composition: global::System.IDisposable
	{
		private readonly Composition _rootM05D20di;
		private readonly object _lockM05D20di;
		private object[] _disposablesM05D20di;
		private int _disposeIndexM05D20di;
		
		private MobiSystems.Services.ServiceHost _singletonServiceHostM05D20di41;
		private MobiSystems.Telemetry.SentryIO.SentryIOTelemetry _singletonSentryIOTelemetryM05D20di36;
		private MobiSystems.Windows.Threading.ProcessRegistry _singletonProcessRegistryM05D20di33;
		private MobiSystems.Logging.Logger2 _singletonLogger2M05D20di40;
		private MobiSystems.Packages.AppConfiguration _singletonAppConfigurationM05D20di35;
		private MobiSystems.Telemetry.SentryIO.TelemetryRuntime _singletonTelemetryRuntimeM05D20di37;
		private MobiSystems.Windows.DeploymentContext _singletonDeploymentContextM05D20di38;
		
		/// <summary>
		/// This constructor creates a new instance of <see cref="Composition"/>.
		/// </summary>
		public Composition()
		{
			_rootM05D20di = this;
			_lockM05D20di = new object();
			_disposablesM05D20di = new object[3];
		}
		
		/// <summary>
		/// This constructor creates a new instance of <see cref="Composition"/> scope based on <paramref name="parentScope"/>. This allows the <see cref="Lifetime.Scoped"/> life time to be applied.
		/// </summary>
		/// <param name="parentScope">Scope parent.</param>
		internal Composition(Composition parentScope)
		{
			_rootM05D20di = (parentScope ?? throw new global::System.ArgumentNullException(nameof(parentScope)))._rootM05D20di;
			_lockM05D20di = _rootM05D20di._lockM05D20di;
			_disposablesM05D20di = parentScope._disposablesM05D20di;
		}
		
		#region Composition Roots
		/// <summary>
		/// Provides a composition root of type <see cref="MobiSystems.ServiceHost.ServiceApplication"/>.
		/// </summary>
		/// <example>
		/// This shows how to get an instance of type <see cref="MobiSystems.ServiceHost.ServiceApplication"/>:
		/// <code>
		/// using var composition = new Composition();
		/// var instance = composition.Root;
		/// </code>
		/// </example>
		public MobiSystems.ServiceHost.ServiceApplication Root
		{
			#if NETSTANDARD2_0_OR_GREATER || NETCOREAPP || NET40_OR_GREATER || NET
			[global::System.Diagnostics.Contracts.Pure]
			#endif
			[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
			get
			{
				var perResolveFuncM05D20di47 = default(System.Func<MobiSystems.Logging.ILogger2>);
				var perResolveFuncM05D20di48 = default(System.Func<MobiSystems.Services.IServiceHost>);
				var perResolveFuncM05D20di49 = default(System.Func<System.Collections.Generic.IEnumerable<MobiSystems.Services.IService>>);
				System.Func<bool, MobiSystems.Services.ServiceHostEnvironment> transientFuncM05D20di1 = [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] (bool registerForRestart) =>
				            {
				
					if (perResolveFuncM05D20di47 == null)
					{
						lock (_lockM05D20di)
						{
							if (perResolveFuncM05D20di47 == null)
							{
								perResolveFuncM05D20di47 = new global::System.Func<MobiSystems.Logging.ILogger2>([global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] () =>
								                    {
								
									EnsureExistenceOf_singletonLogger2M05D20di40();
									var localValueM05D20di2 = _rootM05D20di._singletonLogger2M05D20di40;
								                        return localValueM05D20di2;
								                    });
							}
						}
					}
					
					var localLoggerFactoryM05D20di0 = perResolveFuncM05D20di47;
				
					if (perResolveFuncM05D20di48 == null)
					{
						lock (_lockM05D20di)
						{
							if (perResolveFuncM05D20di48 == null)
							{
								perResolveFuncM05D20di48 = new global::System.Func<MobiSystems.Services.IServiceHost>([global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] () =>
								                    {
								
									if (_rootM05D20di._singletonServiceHostM05D20di41 == null)
									{
										lock (_lockM05D20di)
										{
											if (_rootM05D20di._singletonServiceHostM05D20di41 == null)
											{
												EnsureExistenceOf_singletonLogger2M05D20di40();
												if (_rootM05D20di._singletonProcessRegistryM05D20di33 == null)
												{
													_rootM05D20di._singletonProcessRegistryM05D20di33 = new ProcessRegistry(ProcessRegistryCommonNameProvider.GetName(ServiceApplication.GetProductFamilyName()), (int)ProductType.ManagementService);
													_rootM05D20di._disposablesM05D20di[_rootM05D20di._disposeIndexM05D20di++] = _rootM05D20di._singletonProcessRegistryM05D20di33;
												}
												
												if (perResolveFuncM05D20di49 == null)
												{
													perResolveFuncM05D20di49 = new global::System.Func<System.Collections.Generic.IEnumerable<MobiSystems.Services.IService>>([global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] () =>
													                    {
													
														EnsureExistenceOf_singletonAppConfigurationM05D20di35();
														EnsureExistenceOf_singletonLogger2M05D20di40();
														EnsureExistenceOf_singletonSentryIOTelemetryM05D20di36();
														MobiSystems.Services.IService[] transientM05D20di3;
														{
															var localArrM05D20di11 = new MobiSystems.Services.IService[1] { new MobiSystems.PackageUpdater.Service.PackageUpdaterService(_rootM05D20di._singletonSentryIOTelemetryM05D20di36, _rootM05D20di._singletonLogger2M05D20di40, _rootM05D20di._singletonAppConfigurationM05D20di35) };
														 transientM05D20di3 = localArrM05D20di11;}
														System.Collections.Generic.IReadOnlyCollection<MobiSystems.Services.IService> transientIReadOnlyCollectionM05D20di2;
														{
															var localItemsM05D20di12 = transientM05D20di3;
														 transientIReadOnlyCollectionM05D20di2 = localItemsM05D20di12;}
														var localValueM05D20di5 = transientIReadOnlyCollectionM05D20di2;
													                        return localValueM05D20di5;
													                    });
												}
												
												EnsureExistenceOf_singletonSentryIOTelemetryM05D20di36();
												_rootM05D20di._singletonServiceHostM05D20di41 = new MobiSystems.Services.ServiceHost(_rootM05D20di._singletonSentryIOTelemetryM05D20di36, perResolveFuncM05D20di49, _rootM05D20di._singletonProcessRegistryM05D20di33, _rootM05D20di._singletonLogger2M05D20di40);
											}
										}
									}
									
									var localValueM05D20di4 = _rootM05D20di._singletonServiceHostM05D20di41;
								                        return localValueM05D20di4;
								                    });
							}
						}
					}
					
					var localServiceHostFactoryM05D20di1 = perResolveFuncM05D20di48;
				                return new ServiceHostEnvironment(localLoggerFactoryM05D20di0, localServiceHostFactoryM05D20di1, registerForRestart);
				            };
				return new MobiSystems.ServiceHost.ServiceApplication(transientFuncM05D20di1);
				[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
				void EnsureExistenceOf_singletonLogger2M05D20di40()
				{
					if (_rootM05D20di._singletonLogger2M05D20di40 == null)
					{
						lock (_lockM05D20di)
						{
							if (_rootM05D20di._singletonLogger2M05D20di40 == null)
							{
								{                Logging.Sinks.File localFileSinkM05D20di3= new("c:\\Temp\\service-host.txt", new DefaultLogsCleaner());
								 _rootM05D20di._singletonLogger2M05D20di40 = new LoggerConfiguration()
								                    .WriteTo(localFileSinkM05D20di3)
								                    .CreateLogger();}
								_rootM05D20di._disposablesM05D20di[_rootM05D20di._disposeIndexM05D20di++] = _rootM05D20di._singletonLogger2M05D20di40;
							}
						}
					}
					
				}
				[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
				void EnsureExistenceOf_singletonAppConfigurationM05D20di35()
				{
					if (_rootM05D20di._singletonAppConfigurationM05D20di35 == null)
					{
						lock (_lockM05D20di)
						{
							if (_rootM05D20di._singletonAppConfigurationM05D20di35 == null)
							{
								{                AppConfiguration localConfigurationM05D20di6= new AppConfiguration();
								localConfigurationM05D20di6.Init("MobiSystemsPackaged\\Configuration", "MobiSystems.ServiceHost.appsettings.json");
								 _rootM05D20di._singletonAppConfigurationM05D20di35 = localConfigurationM05D20di6;}
							}
						}
					}
					
				}
				[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
				void EnsureExistenceOf_singletonTelemetryRuntimeM05D20di37()
				{
					if (_rootM05D20di._singletonTelemetryRuntimeM05D20di37 == null)
					{
						if (_rootM05D20di._singletonDeploymentContextM05D20di38 == null)
						{
							_rootM05D20di._singletonDeploymentContextM05D20di38 = new MobiSystems.Windows.DeploymentContext();
						}
						
						{
							var localDeploymentContextM05D20di7 = _rootM05D20di._singletonDeploymentContextM05D20di38;
						
							var localConfigurationM05D20di8 = _rootM05D20di._singletonAppConfigurationM05D20di35;
						
						                TelemetryRuntimeParameters localParametersM05D20di9= new TelemetryRuntimeParameters(
						                    "MobiSystems.ServiceHost",
						localConfigurationM05D20di8["TelemetryHost"],
						localConfigurationM05D20di8["BuildType"],
						localDeploymentContextM05D20di7.DeploymentType.GetDeploymentTypeName(),
						                    (_) => true);
						 _rootM05D20di._singletonTelemetryRuntimeM05D20di37 = new TelemetryRuntime(localParametersM05D20di9);}
						_rootM05D20di._disposablesM05D20di[_rootM05D20di._disposeIndexM05D20di++] = _rootM05D20di._singletonTelemetryRuntimeM05D20di37;
					}
					
				}
				[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
				void EnsureExistenceOf_singletonSentryIOTelemetryM05D20di36()
				{
					if (_rootM05D20di._singletonSentryIOTelemetryM05D20di36 == null)
					{
						lock (_lockM05D20di)
						{
							if (_rootM05D20di._singletonSentryIOTelemetryM05D20di36 == null)
							{
								EnsureExistenceOf_singletonTelemetryRuntimeM05D20di37();
								{
									var localTelemetryRuntimeM05D20di10 = _rootM05D20di._singletonTelemetryRuntimeM05D20di37;
								 _rootM05D20di._singletonSentryIOTelemetryM05D20di36 = new SentryIOTelemetry();}
							}
						}
					}
					
				}
			}
		}
		#endregion
		
		#region API
		/// <summary>
		/// Resolves the composition root.
		/// </summary>
		/// <typeparam name="T">The type of the composition root.</typeparam>
		/// <returns>A composition root.</returns>
		#if NETSTANDARD2_0_OR_GREATER || NETCOREAPP || NET40_OR_GREATER || NET
		[global::System.Diagnostics.Contracts.Pure]
		#endif
		[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
		public T Resolve<T>()
		{
			return ResolverM05D20di<T>.Value.Resolve(this);
		}
		
		/// <summary>
		/// Resolves the composition root by tag.
		/// </summary>
		/// <typeparam name="T">The type of the composition root.</typeparam>
		/// <param name="tag">The tag of a composition root.</param>
		/// <returns>A composition root.</returns>
		#if NETSTANDARD2_0_OR_GREATER || NETCOREAPP || NET40_OR_GREATER || NET
		[global::System.Diagnostics.Contracts.Pure]
		#endif
		[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
		public T Resolve<T>(object tag)
		{
			return ResolverM05D20di<T>.Value.ResolveByTag(this, tag);
		}
		
		/// <summary>
		/// Resolves the composition root.
		/// </summary>
		/// <param name="type">The type of the composition root.</param>
		/// <returns>A composition root.</returns>
		#if NETSTANDARD2_0_OR_GREATER || NETCOREAPP || NET40_OR_GREATER || NET
		[global::System.Diagnostics.Contracts.Pure]
		#endif
		[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
		public object Resolve(global::System.Type type)
		{
			var index = (int)(_bucketSizeM05D20di * ((uint)global::System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(type) % 1));
			ref var pair = ref _bucketsM05D20di[index];
			return pair.Key == type ? pair.Value.Resolve(this) : ResolveM05D20di(type, index);
		}
		
		[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
		private object ResolveM05D20di(global::System.Type type, int index)
		{
			var finish = index + _bucketSizeM05D20di;
			while (++index < finish)
			{
				ref var pair = ref _bucketsM05D20di[index];
				if (pair.Key == type)
				{
					return pair.Value.Resolve(this);
				}
			}
			
			throw new global::System.InvalidOperationException($"{CannotResolveMessageM05D20di} {OfTypeMessageM05D20di} {type}.");
		}
		
		/// <summary>
		/// Resolves the composition root by tag.
		/// </summary>
		/// <param name="type">The type of the composition root.</param>
		/// <param name="tag">The tag of a composition root.</param>
		/// <returns>A composition root.</returns>
		#if NETSTANDARD2_0_OR_GREATER || NETCOREAPP || NET40_OR_GREATER || NET
		[global::System.Diagnostics.Contracts.Pure]
		#endif
		[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
		public object Resolve(global::System.Type type, object tag)
		{
			var index = (int)(_bucketSizeM05D20di * ((uint)global::System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(type) % 1));
			ref var pair = ref _bucketsM05D20di[index];
			return pair.Key == type ? pair.Value.ResolveByTag(this, tag) : ResolveM05D20di(type, tag, index);
		}
		
		[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
		private object ResolveM05D20di(global::System.Type type, object tag, int index)
		{
			var finish = index + _bucketSizeM05D20di;
			while (++index < finish)
			{
				ref var pair = ref _bucketsM05D20di[index];
				if (pair.Key == type)
				{
					return pair.Value.ResolveByTag(this, tag);
				}
			}
			
			throw new global::System.InvalidOperationException($"{CannotResolveMessageM05D20di} \"{tag}\" {OfTypeMessageM05D20di} {type}.");
		}
		#endregion
		
		/// <summary>
		/// <inheritdoc/>
		/// </summary>
		public void Dispose()
		{
			int disposeIndex;
			object[] disposables;
			lock (_lockM05D20di)
			{
				disposeIndex = _disposeIndexM05D20di;
				_disposeIndexM05D20di = 0;
				disposables = _disposablesM05D20di;
				_disposablesM05D20di = new object[3];
				_singletonServiceHostM05D20di41 = null;
				_singletonSentryIOTelemetryM05D20di36 = null;
				_singletonProcessRegistryM05D20di33 = null;
				_singletonLogger2M05D20di40 = null;
				_singletonAppConfigurationM05D20di35 = null;
				_singletonTelemetryRuntimeM05D20di37 = null;
				_singletonDeploymentContextM05D20di38 = null;
			}
			
			while (disposeIndex-- > 0)
			{
				switch (disposables[disposeIndex])
				{
					case global::System.IDisposable disposableInstance:
						try
						{
							disposableInstance.Dispose();
						}
						catch (Exception exception)
						{
							OnDisposeException(disposableInstance, exception);
						}
						break;
				}
			}
		}
		
		/// <summary>
		/// Implement this partial method to handle the exception on disposing.
		/// <summary>
		/// <param name="disposableInstance">The disposable instance.</param>
		/// <param name="exception">Exception occurring during disposal.</param>
		/// <typeparam name="T">The actual type of instance being disposed of.</typeparam>
		partial void OnDisposeException<T>(T disposableInstance, Exception exception) where T : global::System.IDisposable;
		
		private readonly static int _bucketSizeM05D20di;
		private readonly static global::Pure.DI.Pair<global::System.Type, global::Pure.DI.IResolver<Composition, object>>[] _bucketsM05D20di;
		
		static Composition()
		{
			var valResolverM05D20di_0000 = new ResolverM05D20di_0000();
			ResolverM05D20di<MobiSystems.ServiceHost.ServiceApplication>.Value = valResolverM05D20di_0000;
			_bucketsM05D20di = global::Pure.DI.Buckets<global::System.Type, global::Pure.DI.IResolver<Composition, object>>.Create(
				1,
				out _bucketSizeM05D20di,
				new global::Pure.DI.Pair<global::System.Type, global::Pure.DI.IResolver<Composition, object>>[1]
				{
					 new global::Pure.DI.Pair<global::System.Type, global::Pure.DI.IResolver<Composition, object>>(typeof(MobiSystems.ServiceHost.ServiceApplication), valResolverM05D20di_0000)
				});
		}
		
		#region Resolvers
		private const string CannotResolveMessageM05D20di = "Cannot resolve composition root ";
		private const string OfTypeMessageM05D20di = "of type ";
		
		private class ResolverM05D20di<T>: global::Pure.DI.IResolver<Composition, T>
		{
			public static global::Pure.DI.IResolver<Composition, T> Value = new ResolverM05D20di<T>();
			
			public virtual T Resolve(Composition composite)
			{
				throw new global::System.InvalidOperationException($"{CannotResolveMessageM05D20di}{OfTypeMessageM05D20di}{typeof(T)}.");
			}
			
			public virtual T ResolveByTag(Composition composite, object tag)
			{
				throw new global::System.InvalidOperationException($"{CannotResolveMessageM05D20di}\"{tag}\" {OfTypeMessageM05D20di}{typeof(T)}.");
			}
		}
		
		private sealed class ResolverM05D20di_0000: ResolverM05D20di<MobiSystems.ServiceHost.ServiceApplication>
		{
			public override MobiSystems.ServiceHost.ServiceApplication Resolve(Composition composition)
			{
				return composition.Root;
			}
			
			public override MobiSystems.ServiceHost.ServiceApplication ResolveByTag(Composition composition, object tag)
			{
				switch (tag)
				{
					case null:
						return composition.Root;
					
					default:
						return base.ResolveByTag(composition, tag);
				}
			}
		}
		#endregion
		
	}
}

warning CS1570: XML comment has badly formed XML -- 'Expected an end tag for element 'summary'.'

The generated code has invalid XML.

The summary tag starts outside the preprozessor if, and ends inside the preprozessor. I think everything should be inside the preprocessor.

/// <summary>
#if !NET35 && !NET20
    /// Represents the generic type arguments marker for <c>System.IDisposable</c>.
    /// </summary>
    [GenericTypeArgument]
    internal interface TTDisposable: System.IDisposable { }
#endif

Overriding bindings from test project

In a regular ASP.NET project, for example, you can set up your DI via Startup.cs:

services.AddSingleton<IServiceA, ServiceA>()
  .AddSingleton<IServiceB, ServiceB>();

and in the test project, you can do this to override the configured DI for the main project (see here):

builder.ConfigureTestServices(services => services.AddSingleton<IServiceA, MockServiceA>());

Is there a way to do something similar with this library? I have tried the following:
Main project (has no knowledge of test project):

// Composition.cs
public partial class Composition;

// Startup.cs
DI.Setup("Namespace.Composition").Bind<IServiceA>().As(Lifetime.Singleton).To<ServiceA>().Root<IServiceA>();

Test project (references main project):

// BaseTest.cs
DI.Setup("Namespace.Composition")
  .Bind<IServiceA>().As(Lifetime.Singleton).To(_ => Mock.Of<IServiceA>).Root<IServiceA>();

Inspecting the Composition instance and stepping through the code during debugging shows that the test setup isn't overriding the original as I'd like. Is there an approach I should use?

System.InsufficientExecutionStackException: Insufficient stack to continue executing the program safely. This can happen from having too many functions on the call stack or function on the stack using too much stack space.

Hello,

I would like to migrate my big Winforms project from SimpleInjector to Pure.DI. During migration, if I have more than 100 bindings I get this error:

Warning	CS8785	Generator 'SourceGenerator' failed to generate source. It will not contribute to the output and compilation errors may occur as a result. Exception was of type 'HandledException' with message 'System.InsufficientExecutionStackException: Insufficient stack to continue executing the program safely. This can happen from having too many functions on the call stack or function on the stack using too much stack space.    at System.Runtime.CompilerServices.RuntimeHelpers.EnsureSufficientExecutionStack()    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.LocalBinderFactory.VisitInvocationExpression(InvocationExpressionSyntax node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.LocalBinderFactory.VisitInvocationExpression(InvocationExpressionSyntax node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.LocalBinderFactory.VisitInvocationExpression(InvocationExpressionSyntax node)    at 

alker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Pure.DI.Core.MetadataWalker.VisitInvocationExpression(InvocationExpressionSyntax node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Pure.DI.Core.MetadataWalker.VisitInvocationExpression(InvocationExpressionSyntax node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Pure.DI.Core.MetadataWalker.VisitInvocationExpression(InvocationExpressionSyntax node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Pure.DI.Core.MetadataWalker.VisitInvocationExpression(InvocationExpressionSyntax node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Pure.DI.Core.MetadataWalker.VisitInvocationExpression(InvocationExpressionSyntax node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Pure.DI.Core.MetadataWalker.VisitInvocationExpression(InvocationExpressionSyntax node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Pure.DI.Core.MetadataWalker.VisitInvocationExpression(InvocationExpressionSyntax node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Pure.DI.Core.MetadataWalker.VisitInvocationExpression(InvocationExpressionSyntax node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.DefaultVisit(SyntaxNode node)    at Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker.Visit(SyntaxNode node)    at Pure.DI.Core.MetadataBuilder.<GetMetadata>d__7.MoveNext()    at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)    at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)    at Pure.DI.Core.MetadataBuilder.Build(IExecutionContext executionContext)    at Pure.DI.Core.SourceBuilder.Build(IExecutionContext executionContext)    at Pure.DI.Core.Generator.Generate(IExecutionContext context)'	XXX C:\xx\xx.csproj	1	Active

If you need more information, please ask.

Unclear error DI007 when compilation errors were occurred

Shroedinger cat sample. Create separate classlib and move 3 classes to there State, ICat and ShroedingersCat
following error

error DI007: Sample.ShroedingersCat is not inherited from Sample.ICat

If I move ShroedingersCat back to console app, everything working again.

Splat benchmark

Discussed in #23

Originally posted by mysteryx93 February 27, 2023
Could you add Splat to the benchmark? Requires Splat.DI.SourceGenerator

Pure.DI and Splat actually look pretty similar, I'm curious as to the difference between the two.

Since both are source generators, performance should be pretty close. Any feature that one has over the other?

Class library usage example

Hi,

Nice project. I've read through and I'm not clear on one quite common use case.

Let's say I'm creating a class library that wants to expose a utility method to inject dependencies into a container, how would you recommend achieving that?

For example, my library might expose a number of interfaces with concrete implementations, and want to expose an extension method like:

    public static class DIExtensions
    {
        public static IConfiguration UsingLib(this IConfiguration configuration) => configuration
            .Bind<IA>.To<A>()
            .Bind<IB>.To<B>();
    }

Which you would use like:

        private static void Setup() => DI.Setup()
			.UsingLib()
			.Bind...

However IConfiguration is internal, so you can't even do this kind of thing even in the same project.

I understand that the point of DI is that the classes (in my example 'A' and 'B') don't need to know about each other, and that Pure.DI "isn't a framework", however, it is often useful to have a chunk of 'common usage' code for convenience to prevent a lot of boiler-plate code needing to be added each time.

C#12 collection expressions are not supported

C# 12 brings a new way to instantiate collections via brackets like this [1,2,3]. Currently this is not supported so that Bind<IDependency>(tags: "tag").To<Dependency>() works fine but Bind<IDependency>(tags: ["tag"]).To<Dependency>() fails with "DIE001 ["tag"] must be a constant value of type System.Object.". Btw, C# treats it as a constant, you can use such a value in attributes, etc.

Performance of the source generator.

Hello!

I'm not sure about these topics, so I post them here only fyi.

  1. If I understand the things right, then https://github.com/DevTeam/Pure.DI/blob/master/src/Pure.DI/SourceGenerator.cs#L27 means you are collecting all syntax nodes inside a whole source tree of the project (solution?). Looks like it will have scaling problems. What are performance of this approach? Is there any performance benefits in comparison with old ISourceGenerator?
  2. Again, If I not missed something, pushing GeneratorSyntaxContext into generator pipeline https://github.com/DevTeam/Pure.DI/blob/master/src/Pure.DI/SourceGenerator.cs#L28 renders the generator to be completely non incremental. Additional context can be found here: zompinc/sync-method-generator#20

As an author of similar library https://github.com/lsoft/DpDtInject I found no way to effiently apply incremental source generators for the task of DI. If I wrong, could you share where did I miss?

Good luck! Thanks!

Resolving transient dependencies with MS DI is really slow

If I use Pure.DI to inject and resolve dependencies, then it is really quick.
If I use MS DI to inject and resolve dependencies, then it is faster.
If I use inject with Pure.DI and resolve with MS DI, it is really slow.

Method Mean Error StdDev Ratio RatioSD
PureDI 5.278 ns 0.1770 ns 0.2857 ns 1.00 0.00
DotNet 45.698 ns 0.9411 ns 1.0837 ns 8.67 0.58
PureDIResolvedWithMS 71.330 ns 0.9999 ns 0.9353 ns 13.60 0.86

I would have expected injecting with Pure.DI and resolving with MS DI to be somewhere in the middle, not significantly worse. Do you have any Ideas on what is happening?

Code:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using Microsoft.Extensions.DependencyInjection;
using Pure.DI;

var summary = BenchmarkRunner.Run<DIComparison>();

public class DIComparison
{
    private IServiceProvider _microsoftServiceProvider;
    private IServiceProvider _pureDIAndMSServiceProvider;
    private IServiceProvider _handCraftedServiceProvider;

    [GlobalSetup]
    public void Setup()
    {
        _microsoftServiceProvider = new ServiceCollection()
            .AddTransient<Something>()
            .AddTransient<SomethingElse>()
            .BuildServiceProvider();

        _pureDIAndMSServiceProvider = new ServiceCollection()
            .AddPureDIClass()
            .BuildServiceProvider();
    }

    [Benchmark(Baseline = true)]
    public Something PureDI() => PureDIClass.Resolve<Something>();

    [Benchmark(Baseline = false)]
    public Something DotNet() => _microsoftServiceProvider.GetRequiredService<Something>();

    [Benchmark]
    public Something PureDIResolvedWithMS() => _pureDIAndMSServiceProvider.GetRequiredService<Something>();
}

public class Something 
{
    public Something(SomethingElse somethingElse) { }
}

public class SomethingElse { }

public static partial class PureDIClass
{
    public static void Setup() => DI.Setup()
        .Bind<Something>().As(Lifetime.Transient).To<Something>()
        .Bind<SomethingElse>().As(Lifetime.Transient).To<SomethingElse>();
}

IsRegistered(Type type) check

The current asp.net integration seems not to give compile-time check advantage over the default asp container because it works based on OnCannotResolve hint which disables compile-time checks. I'd use another approach where you combine pure and asp containers instead of integration:

class CustomServiceProvider : IServiceProvider 
{
    private readonly Composition _composition;
    private readonly IServiceProvider _aspContainer;

    public object? GetService(Type type)
    {
        return _composition.HasRegistrationFor(type) ? _composition.Resolve(type) : _aspContainer.GetService();
    }
}

Then the pure container could be without any hints or extensions preventing from compile checks. You just need to take care of proper dependencies' disposal. However, I don't see a way to do that without catching an exception which is super slow. I'd suggest extending Pure.DI with such a possibility. It might be a registration check or Resolve method that doesn't throw implemented by a new hint or so.

Please think of this suggestion. In case you like/okay with the proposal, I can try to contribute if you give implementation details (method/hint names, etc).

Lazy cyclic dependencies are no longer possible in v2.0

Description

Since v2.0, it is no longer possible to have a cyclic dependency where one of the dependencies is lazy resolved.
this results in the following error:

error DIE001: A cyclic dependency has been found Pure.DI.Example.Dependency <-- System.Lazy<Pure.DI.Example.IDependency2> <-- System.Func<Pure.DI.Example.IDependency2> <-- Pure.DI.Example.Dependency2 <-- Pure.DI.Example.Dependency.

In v1, code was generated to resolve the lazy dependency lazily.

Example

public class Dependency : IDependency
{
    public Dependency(Lazy<IDependency2> dependency2) { }
}

public class Dependency2 : IDependency2
{
    public Dependency2(IDependency dependency) { }
}

public partial class Composition
{
    public static void Setup() =>
        DI.Setup(nameof(Composition))
            .Bind<IDependency>().To<Dependency>()
            .Bind<IDependency2>().To<Dependency2>()
            .Bind<IService>().To<Service>().Root<IService>("Root");
}

Repository

https://github.com/Yves-Be/Pure.DI.LazyCircularDependency

Running benchmarks on ServiceCollection results in an InvalidCastException

$ .\Pure.DI.Benchmark.exe ServiceCollection
Unhandled exception. System.InvalidCastException: Unable to cast object of type 'Pure.DI.Benchmark.Benchmarks.ServiceCollection' to type 'Pure.DI.Benchmark.BenchmarkBase'.
at Pure.DI.Benchmark.Program.Main(String[] args) in C:\repos\Pure.DI\Pure.DI.Benchmark\Program.cs:line 26

I'm going to attempt a PR.

Ability to register composition roots with fewer API calls

Right now:

DI.Setup(nameof(Composition))
  .Bind<IService>().To<Service>()
  .Bind<IService>("Other").To<OtherService>()
  .Root<IService>("MyRoot")
  .Root<IService>("SomeOtherService", "Other");

An API such as this is proposed:

DI.Setup(nameof(Composition))
  .RootBind<IService>("MyRoot").To<Service>()
  .RootBind<IService>("SomeOtherService", "Other").To<OtherService>()

Thus we could add a new API method to IConfiguration like:

IBinding RootBind<T>(string name = "", object tag = null, RootKinds kind = RootKinds.Default);

For integration tests and local debugging, you can use this project.

Major changes are likely to be here.

We also need an example in the documentation. It can be done here.

Singletons

Hi,

When specifying singletons, it appears, from the generated code, that most of the Resolve() methods will retrieve the generated static internal readonly [T] Shared field from the generated Singleton[T] class; as you'd expect.

However, the generated Resolve[T]() methods will create a new instance of the type T. (I've used [T] here to indicate a direct substitution so as to not confuse with generics, etc.)

Using the ShroedingersCat sample, but using the following Setup:

        private static void Setup() => DI.Setup()
            .Default(Singleton) // Use singletons
            // Represents a quantum superposition of 2 states: Alive or Dead
            .Bind<State>().To(_ => (State)Indeterminacy.Next(2))
            // Represents schrodinger's cat
            .Bind<ICat>().To<ShroedingersCat>()
            // Represents a cardboard box with any content
            .Bind<IBox<TT>>().To<CardboardBox<TT>>()
            // Composition Root
            .Bind<Program>().To<Program>();

The resulting ResolveSampleProgram is generated as:

        [System.Runtime.CompilerServices.MethodImplAttribute((System.Runtime.CompilerServices.MethodImplOptions)256)]
        public static Sample.Program ResolveProgram()
        {
            return // Bind<Sample.Program>().As(Singleton).To<Sample.Program>()
            new Sample.Program(...);
        }

When I'd expect it to be:

[System.Runtime.CompilerServices.MethodImplAttribute((System.Runtime.CompilerServices.MethodImplOptions)256)]
        public static Sample.Program ResolveProgram()
        {
            return // Bind<Sample.Program>().As(Singleton).To<Sample.Program>()
            SingletonSampleProgram.Shared;
        }

Indeed the SingletonSampleProgram.Shared is generated and used in the ResolversTable, but this final step creates a new instance.

Having specified a singleton pattern, I wouldn't expect instances to be 'leaked' in this way?

Saving files with Code Cleanup with Pure.DI - performance issue

Hello, I have a large Winforms project with about 500 DI classes. When I save files, Visual Studio stuck on (for 50-500s):
pure00
In VS 2022 (17.5.1) I have enabled Options -> Text Editor -> Codde Cleanup -> Run Code Cleanup profile on Save. In profile, I have only: Remove unnecessary Imports.

Using the second VS instance I attached it to the first VS instance and dump active Tasks:
pure01

It looks like, Pure.DI unnecessarily runs during CodeCleanup. When I uninstall Pure.DI Code Cleanup runs very fast.

PureDI logs show only:
WRN Tracer: The path is empty.

How can I help you solve this problem? I was able to successfully migrate my entire project to PureDI. Unfortunately, with such performance in VS it is impossible to work ...

Rename project

I've been learning about this project and I'm excited to try it out. I think it has great potential. Unfortunately, the name is pretty bad. It's not imaginative nor is it searchable and discovery is key. Even removing the dot and using PureDI makes it more searchable.

Some other alternatives might be:

  • PurDI (maybe pronounced purr-dee or pure-dee)
  • DIGen
  • InjectGen
  • Injectinator

Keep up the good work!

Confusing error if parentheses are used when registering a factory method

Hi there,

I just spent a good half an hour trying to figure out what I was doing wrong registering a factory method

.Bind<ILoggerFactory>().As(Lifetime.Singleton).To((ctx) =>
{
    ctx.Inject(out ILogger logger);
    return new LoggerFactory()
        .AddSerilog(logger);
})

This code does not compile with the error

Error	DIE003	
The DI.Setup(nameof(CompositionRoot))
        .Bind<ILoggerFactory>().As(Lifetime.Singleton).To((ctx) =>
        {
            ctx.Inject(out ILogger logger);
            return new LoggerFactory()
                .AddSerilog(logger);
        }) is not supported.

After a bit of playing around, it seems that the problem is actually the parenthesis (ctx) => { } in the registration, as they interfere with the source generation and removing them allows the source generator to continue correctly.

Admittedly they're completely superfluous, but I honestly didn't even notice they were there in the sea of red squiggles in Visual Studio.

Environment information:
.net 8.0.101
C# LangVersion default (11)
Pure.DI version 2.0.45 (latest as of this report)

Generated Code emits Warnings from StyleCop rules

Sadly the generated code hits quite a few warnings in our codebase

Currently you deactivate the warnings from analyzers and JetBrains one by one by hand:

Instead you should make it clear to the Analyzers that the code is auto-generated and therefore the style is not checked:

https://shishkin.wordpress.com/2008/07/08/stylecop-how-to-ignore-generated-code/

You can add the comment on the start of the file:

// <auto-generated/>

I am not sure how necessary the file type is, but generated code usually has one of the following file endings, this could also help top avoid warnings from automatic tooling:

*.generated.cs
*.g.cs
*.Designer.cs

Asp.Net Example

Firstly, been watching this repo to see what 2.0 was going to bring and excited to see the possibilities! Awesome work.

Second, I didn't see it in the list in the readme, but do you have an example for how this might be used in an ASP.Net 6 project. This seems like a big chunk of what would need to happen to make it AoT compatible (plus the stuff that's coming in .Net 8).

Invalid code with duplicate field names is generated in case if multiple roots are dependent on external argument

Description

I am using Pure.DI 2.1.1 and I have a registration scheme with multiple roots exposed.
I pass IExternalDependency as an argument to the Composition constructor, and multiple types have a dependency on this IExternalDependency.
However, this configuration leads to invalid code being generated, and I see the following errors during the compilation phase:

Error CS0100: The parameter name 'externalDependency' is a duplicate
Error CS0102: The type 'Composition' already contains a definition for '_argM02D18di_externalDependency'

Example

DI.Setup("Composition")
    .Bind<IService1>().As(Lifetime.Singleton).To<Service1>()
    .Bind<IService2>().As(Lifetime.Singleton).To<Service2>()
    .Bind<IRootService>().As(Lifetime.Singleton).To<RootService>()
    .Arg<IExternalDependency>("externalDependency")
    .Root<IService1>("Service1")
    .Root<IRootService>("Root");

Repository

tests/Pure.DI.UsageTests/Basics/ArgumentsScenario2.cs

Issue with explicit interface implementation

I have this class

    public class AppConfiguration : IConfiguration
    {
        string IConfiguration.this[string key] { get => configuration[key]; set => configuration[key] = value; }
     }

and this registration

CSharp

.Bind<Microsoft.Extensions.Configuration.IConfiguration>().As(Singleton).To(ctx => 
{
    AppConfiguration configuration = new AppConfiguration();
    ....
    return configuration;
})


.Bind().As(Singleton).To<TelemetryRuntime>(ctx => 
{
    ctx.Inject(out Microsoft.Extensions.Configuration.IConfiguration configuration);
    var a = configuration["TelemetryHost"],
    ....
})

Source generator generate a code

var localConfigurationM05D20di9 = _rootM05D20di._singletonAppConfigurationM05D20di35;
var a = localConfigurationM05D20di9["TelemetryHost"];

The reference type of localConfigurationM05D20di9 is AppConfiguration and not IConfiguration. Because of explicit interface implementation, the indexer is not found on AppConfiguration type instance and this fails to compile.

May be the correct generated code should look like this?

var localConfigurationM05D20di9 = (IConfiguration)_rootM05D20di._singletonAppConfigurationM05D20di35;
var a = localConfigurationM05D20di9["TelemetryHost"];

DIE999: An unhandled error has occurred. Collection was modified. (Visual Studio)

Hi,
I'm trying to build the Pure.DI.Example project in Visual Studio 2022,
but I'm getting the following error when the code generation runs:

Generator....................... DIE999: An unhandled error has occurred.
Collection was modified; enumeration operation may not execute.

I've tested several versions (2.0.7, 2.0.10, and 2.0.11-beta2), but they all seem to have the same issue.
A dotnet build does seem to work, so it might be related to Visual Studio.

Compilation error Microsoft.CodeAnalysis.CSharp.CSharpCompilation.Update

I did try

  1. create new console app
  2. copy code from ShroedingersCat sample.
  3. Add reference to Pure.DI
    and receive following error.
1>C:\Program Files\dotnet\sdk\7.0.100-preview.5.22267.11\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.RuntimeIdentifierInference.targets(216,5): message NETSDK1057: You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy
1>Skipping analyzers to speed up the build. You can execute 'Build' or 'Rebuild' command to run analyzers.
1>CSC : error DI002: System.ArgumentException: Inconsistent syntax tree features Parameter name: trees
    at Microsoft.CodeAnalysis.Compilation.SyntaxTreeCommonFeatures(IEnumerable`1 trees)
    at Microsoft.CodeAnalysis.CSharp.CSharpCompilation.Update(ReferenceManager referenceManager, Boolean reuseReferenceManager, SyntaxAndDeclarationManager syntaxAndDeclarations)
    at Pure.DI.Core.MetadataBuilder.Build(IExecutionContext executionContext)
    at Pure.DI.Core.SourceBuilder.Build(IExecutionContext executionContext)
    at Pure.DI.Core.Generator.Generate(IExecutionContext context)
1>D:\d\scratch\ConsoleApp1\ConsoleApp1\Program.cs(13,11,13,15): error CS0246: The type or namespace name 'Pure' could not be found (are you missing a using directive or an assembly reference?)
1>D:\d\scratch\ConsoleApp1\ConsoleApp1\Program.cs(14,18,14,22): error CS0246: The type or namespace name 'Pure' could not be found (are you missing a using directive or an assembly reference?)

after I rebuild project, everything start working.

SyntaxTree is not part of the compilation (Parameter 'syntaxTree') error in .NET SDK 7.0.304

Microsoft.CSharp.Core.targets(80,5): error : Unhandled exception. System.ArgumentException: SyntaxTree is not part of the compilation (Parameter 'syntaxTree') Microsoft.CSharp.Core.targets(80,5): error : at Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetSemanticModel(SyntaxTree syntaxTree, Boolean ignoreAccessibilit y) Microsoft.CSharp.Core.targets(80,5): error : at Microsoft.CodeAnalysis.Diagnostics.SuppressMessageAttributeState.IsDiagnosticSuppressed(Diagnostic diagnostic, Sup pressMessageInfo& info) Microsoft.CSharp.Core.targets(80,5): error : at Microsoft.CodeAnalysis.Diagnostics.SuppressMessageAttributeState.ApplySourceSuppressions(Diagnostic diagnostic)

Disposable Instances Handling

Hi there,

I have a question regarding the disposable instances created by the DI. I've noticed that these disposable instances are only disposed automatically if their lifetime is set to singleton. Is this behavior intended or is it a bug?

Additionally, I've been exploring workarounds and came across this readme, but I'm unsure where to integrate the following code:

// Disposal of instances in reverse order
while (disposables.TryPop(out var disposable))
{
    disposable.Dispose();
}

Any guidance on where to incorporate this code would be greatly appreciated.

Thank you in advance for your assistance!

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.