Closed schuettecarsten closed 4 years ago
Yep, there are options to achieve that, you can mark your singletons as "isolated" scopes with the DefinesScope()
option, then you can register their dependencies with Scoped lifetime, so the singletons will get their own dependency instances.
Like:
class Singleton1
{
public SingletonDep Dep { get; set; }
public Singleton1(SingletonDep dep) { this.Dep = dep; }
}
class Singleton2
{
public SingletonDep Dep { get; set; }
public Singleton2(SingletonDep dep) { this.Dep = dep; }
}
container.Register<Singleton1>(c => c.WithSingletonLifetime().DefinesScope())
.Register<Singleton2>(c => c.WithSingletonLifetime().DefinesScope())
.RegisterScoped<SingletonDep>();
var isSame = container.Resolve<Singleton1>().Dep == container.Resolve<Singleton2>().Dep; //false
So with this, you will get individual SingletonDep
instances bound to the lifetime of your singletons.
Is this the thing you are searching for?
Let me know if you have further questions or if this isn't what you need.
That looks good, but DefinesScope()
needs a parameter in my version. Is null
valid here or do I need to generate a custom (unique) scope name myself for each registration?
Another question, if I use DefinesScope()
, it looks like a custom registration directly on the Container is not resolved. The following registration, that is done at the end of all other registrations, is not used when resolving a service that uses DefinesScope()
:
container.RegisterTypes(new Type[]
{
typeof(IServiceLocator),
typeof(IServiceProvider),
}, null, context => context
.ReplaceExisting()
.WithInstance(serviceLocator)
.WithoutDisposalTracking());
Ah yeah, sorry, the parameterless option is only available in the prerelease version, it's not released yet, you can use a custom scope name with the stable version for now.
As I can see the problem could be that you are using the RegisterTypes()
to register interface types, but the way it works is that it only accepts implementations and auto-discovers their implemented interfaces and base classes to register for. So it completely skips this configuration section, because the types you pass are all interfaces.
The thing you want to achieve can be configured like this:
container.Register<IServiceLocator>(serviceLocator.GetType(), context => context
.WithInstance(serviceLocator)
.AsServiceAlso<IServiceProvider>()
.ReplaceExisting()
.WithoutDisposalTracking());
Just for the note, if the IServiceProvider
you are using is from the System
namespace it's automatically mapped to the container (the current resolution scope actually), you don't have to register it manually.
Sorry if the behavior caused confusion, maybe the docs should be clearer.
Even if I use your code, it does not work. My sample was reduced meta-code, here is the original one. The factory method is never called.
container.Register<IServiceLocator>(typeof(StashboxObjectProvider),
context => context
.WithFactory(delegate(IDependencyResolver resolver)
{
return new StashboxObjectProvider(container, resolver);
})
.AsServiceAlso<IObjectProviderRoot>()
.AsServiceAlso<IObjectProvider>()
.AsServiceAlso<IServiceProvider>()
.ReplaceExisting()
.WithoutDisposalTracking());
Even other versions do not work:
container.Register<StashboxObjectProvider>(context => context
//.WithInstance(objectProvider)
.WithFactory(delegate(IDependencyResolver resolver)
{
return new StashboxObjectProvider(container, resolver);
})
.AsImplementedTypes()
.ReplaceExisting()
.WithoutDisposalTracking());
Ah I see, could you please send me the code of the StashboxObjectProvider
and a class which shows the usage of it?
Thanks!
With which type are you referring to this registration? Are you referring to it with IServiceProvider
as a dependency? The only thing I could think that the container chooses the current resolution scope as IServiceProvider
instead of your implementation (because it implements System.IServiceProvider
) and that is why your factory never called. If this is the case then I can make this feature configurable, but have you considered using the current scope as IServiceProvider
dependency?
Just a quick info, this problem is still there with latest 3.1-preview.
I think I was able to figure out the root cause. I hope I can explain it correctly, as Stashbox is quite complex here and it took me some time to debug and understand what's going on.
The problem is here (ResolutionStrategy.cs, line 29++):
if (resolutionContext.ResolutionScope.HasScopedInstances)
{
var scopedInstance = resolutionContext.ResolutionScope
.GetScopedInstanceOrDefault(typeInformation.Type, typeInformation.DependencyName);
if (scopedInstance != null)
return resolutionContext.CurrentScopeParameter
.CallMethod(Constants.GetScopedInstanceMethod,
typeInformation.Type.AsConstant(),
typeInformation.DependencyName.AsConstant())
.ConvertTo(typeInformation.Type);
}
The problem is that resolutionContext.ResolutionScope
is the current scope that was used to resolve the service from Stashbox. But if the requested service has a singleton or named scope lifetime, then the singleton-/named scope must be used here, not the current scope.
In my code, I create a scope like this:
this.dependencyResolver = dependencyResolver
.BeginScope(name, attachToParent)
.PutInstanceInScope<IServiceLocator>(this, true)
.PutInstanceInScope<IServiceProvider>(this, true)
.PutInstanceInScope<IObjectProvider>(this, true)
.PutInstanceInScope<IObjectProviderScope>(this, true);
I put my IObjectProvider
instance into the scope to make sure that it is returned. This works in normal cases, but fails when a different scope is used at resolution time. In this case, Stashbox looks into my scope, finds an IObjectProvider
instance and generates the expression. When the expression is executed, it uses the lifetime scope, which was created by Stashbox and does not know anything about an IObjectProvider
instance.
This works with latest changes for #77, so I close this issue.
I register several services with Singleton lifetime. That works so far. But all singletons share their dependencies. Is it possible to register a singleton service and tell Stashbox to use a dedicated, "isolated" scope for this service?