-
Notifications
You must be signed in to change notification settings - Fork 50
Scope management enhancement #109
Comments
I definitely understand the need for alternatives since I'm not happy with the current situation either. The main reason for this pattern was proper support for
I'm open for every solution to tackle this issue. In my projects I work around this by declaring commonly used services as computed properties in my view model base class but I do understand that this is not a solution that fits every use case.
It surely would however I would love to hear alternative solutions as well. |
Here are a few thought that I have regarding alternative options to handle the scope management.
New My first thought would be to replace the implementation of I have not dug very deep but that seems possible, for instance using This might however be a source of unexpected side effects, for instance if another library expects the original behavior from the Register the view-modes within Another option might be to register the view-models within services.AddMvvm(options =>
{
options.MvvmServices
.AddViewModel<MyViewModel1>(ViewModelScope.Application)
.AddViewModel<MyViewModel2>(ViewModelScope.View);
}); ( The Thinking a bit more about it, MvvmBlazor could actually use its own IServiceProvider implementation without the need to create a scope. The implementation would search first the application's service provider, then it's own list... Using a factory to control the scope: The services could be registered to the public static class DIExtensions
{
public static IServiceCollection AddMvvmServices(this IServiceCollection services)
{
services.AddScoped<ParentScopeAccessor>();
}
// Register a service that will always be resolved at application scope
public static IServiceCollection AddApplicationScoped<TService>(this IServiceCollection services)
{
return services.AddScoped<TService>(serviceProvider =>
{
ParentScopeAccessor accessor = serviceProvider.GetRequiredService<ParentScopeAccessor>();
IServiceProvider = accessor.ServiceProvider ?? serviceProvider;
return serviceProvider.GetRequiredService<TService>();
});
}
// That method should be used from MvvmComponentBase to create the scope
internal static IServiceScope CreateScope(this IServiceProvider serviceProvider)
{
var scope = serviceProvider.CreateScope();
// Give an access to the ServiceProvider from the parent scope from within that child scope
scope.GetRequiredService<ParentScopeAccessor>().ServiceProvider = serviceProvider;
return scope;
}
}
public class ParentScopeAccessor
{
public ServiceProvider? { get; set; }
} This is however not a viable solution until I have found a way to make sure that the service is disposed from the correct scope (provided that this can be done...). |
I have pushed the IMvvmServiceFactory implementation on my repo: |
Looks good to me, I'm gonna make a deep dive into this topic on the weekend. However, we need to ensure a solution that is non-breaking. I don't want to introduce any breaking changes outside of major upgrades and they should be aligned together with .NET releases. |
I made my thoughts about your solutions:
I would propose a different solution: decorated constructor parameters. Consider this to be pseudo code public class MyViewModel : ViewModelBase
{
public MyViewModel([InjectFromScope] MyScopedService scoped, [InjectFromRoot] MyRootService root) {}
} To prevent breaking changes or a cluttered constructor a default injection strategy would be specified public void ConfigureServices(IServiceCollection services)
{
services.AddMvvm(o => o.DefaultInjectionStrategy = InjectionStrategy.Scoped);
} This would allow to toggle the default behavior and would only require to decorate parameters that fall out of the default strategy. public class MyViewModel : ViewModelBase
{
public MyViewModel(MyScopedService scoped, [InjectFromRoot] MyRootService root) {}
} You could specify |
This is actually the same solution I came to! I have regenerated the nuget packages from the code from my fork, and injected a custom For sure, having a default strategy is even better. My implementation need some rework and might have some flaws: I still haven't done any unit testing as our project is still in a POC stage. I am really busy with several other aspects of my work at the moment, but if you think that this could be of any help I will extract that part of the code in a distinct project and share it. |
I would argue against that. Deeper down the injection tree the services should be resolved as usual depending on the scope of the root service.
Same for me but this shouldn't be too hard to implement. I will try to make a first implementation on the weekend. |
Feature description
Hello,
I am in the process of evaluating the technical solutions for a new and relatively large Blazor/WASM project.
We are going to use MVVM, and on this topic I think that MvvmBlazor could help us save a lot of time and energy.
However the fact that all services within a view are resolved to a scope limited to that view could be an issue for us.
I understand that this behavior might be the best one in the general case, and that there is a workaround using the RootServiceProvider.
But this is an option that we might have to rely on quite a lot, and that would likely make our code much less clear.
I would like to suggest a change that would introduce some kind of "hook" so that the services resolution could be customized:
In the
MvvmComponentBase
class, theScopeFactory
would be declared asIMvvmServiceScopeFactory
instead ofIServiceScopeFactory
.The default implementation for that interface would be:
It would be registered in
AddMvvm()
The current behavior would be unchanged.
I have actually already tested that solution, and implemented a custom
IMvvmServiceScopeFactory
in a test project.That implementation relies on an attribute on the classes and constructors parameters (
ViewScopeAttribute
) to decide on which scope they should be resolved.I can send a pull request if you think that this feature would benefit to the project.
There should be many alternative solutions to achieve a similar result, we can also discuss them if you are not convinced by this one.
Regards,
Code sample
No response
The text was updated successfully, but these errors were encountered: