IDataListSource only supports injection of singletons?
See original GitHub issueWhich Contentment version are you using?
4.1.1
Which Umbraco version are you using? For example: 8.14.1 - don’t just write v8
10.0.1
Bug summary
When using a custom .NET datasource (ie. a class implementing IDataListSource
) if I try to inject a class into the constructor that isn’t a singleton then I get a boot error telling me:
Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: DataSources.AtsTeamDataSource Lifetime: Singleton ImplementationType: DataSources.AtsTeamDataSource': Cannot consume scoped service 'Services.IJobBoardDataService' from singleton 'DataSources.AtsTeamDataSource'.)
From what I gather it is because my custom IDataListSource
called AtsTeamDataSource
is injecting (via constructor) the service IJobBoardDataService
which is a scoped service. If I register IJobBoardDataSource
as a singleton then it works (but I can’t really do that as this service cannot be a singleton).
public class AtsTeamDataSource : IDataListSource
{
private readonly IJobBoardDataService dataService;
public AtsTeamDataSource(IJobBoardDataService dataService)
{
this.dataService = dataService;
}
}
In my IComposer I’m doing:
services.AddScoped<IJobBoardDataService, JobBoardDataService>();
Steps to reproduce
Create a NET datasource that implements IDataListSource
In the constructor of this data source inject a service that is not scoped as a singleton.
Expected result / actual result
I’d expect to be able to inject services that are either scoped or transient.
I’m not quite sure where the AtsTeamDataSource
gets registered from? I’m not registering it as a singleton, so I assume Contentment is. I did try looking in the source, but couldn’t really figure out. I guess I’m looking for whether there is a way it could be registered as transient or something? Any ideas?
Do you have Umbraco ModelsBuilder enabled?
- Yes, it is enabled.
What browsers are you seeing the problem on?
Chrome
Issue Analytics
- State:
- Created a year ago
- Comments:5 (4 by maintainers)
Top GitHub Comments
Hi! Wanted to time in and offer another way of doing this instead of using the Lazy<> way. If you want to retrieve non-singleton classes then you can also inject
IServiceScopeFactory
in your constructer and use it like this when you need to retrieve the service:This would then work for all lifecycles without needing any changes from @leekelleher.
Ofcourse we can also change the interface to accept a IServiceProvider, but that would then be a breaking change. I also don’t think this happens that often although I could be wrong.
Or maybe you can take a look at how Middleware currently functions. By default, you use the
Task Invoke(HttpContext context)
and then then do your stuff and continue the middleware train. However, middlewares are also singletons and suffer from the same problem that Dan is having here. But with middleware you are able to use DI in your Invoke method (https://github.com/patrickdemooij9/SeoToolkit.Umbraco/blob/4155a59ff7300a37981c6c0efb2935d635d3f923/src/SeoToolkit.Umbraco.Sitemap.Core/Middleware/SitemapMiddleware.cs#L30) so that you can retrieve your scoped/transient services.Closing this ticket off, as I’m unsure if there’s anything I could do within Contentment’s
ContentmentListItemCollectionBuilder
to resolve or workaround this.Looking at @patrickdemooij9’s comment above, I recalled that I am already taking a similar approach with the Umbraco Tags data-source, e.g. using
HttpContext.RequestServices.GetRequiredService<ITagQuery>()
to get the scoped service. ref: https://github.com/leekelleher/umbraco-contentment/blob/4.0.0/src/Umbraco.Community.Contentment/DataEditors/DataList/DataSources/UmbracoTagsDataListSource.cs#L76