devteam / pure.di Goto Github PK
View Code? Open in Web Editor NEWPure DI for .NET
License: MIT License
Pure DI for .NET
License: MIT License
It is required for #46
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>();
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
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
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
{
}
}
}
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
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<MobiSystems.ServiceHost.ServiceApplication>()</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
}
}
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
Usage:
public static (T1, T2) Resolve<T1, T2>() =>
ResolveInContext(() => ((T1)Resolver<T1>.Resolve(), (T2)Resolver<T2>.Resolve()))
Arguments must be taken into account. ResolveInContext should be private
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?
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.
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.
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?
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 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.
Hello!
I'm not sure about these topics, so I post them here only fyi.
ISourceGenerator
?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!
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>();
}
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).
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.
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");
}
$ .\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.
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.
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?
Hello, I have a large Winforms project with about 500 DI classes. When I save files, Visual Studio stuck on (for 50-500s):
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:
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 ...
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:
Keep up the good work!
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)
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
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).
do not support Service Dynamic loading from dll Library?
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'
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");
like this:
var objs=ResolveAll();
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"];
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.
I did try
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.
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)
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!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.