Custom srach binder cannot access the app DI services
mahdighorbanpourptw opened this issue · comments
Hi @xuzhg,
I read the article about implementing a custom ISearchBinder and I enjoyed it. I am facing the following issue. In your example, the list of categories for example is a private static list. What if we want to get it from another service which is already registerd to the service collection? I made these changes in your example:
I created a simple service class for categories and I registered it in the startup DI
public interface ICategoriesRepository
{
HashSet<string> GetAll();
}
public class CategoriesRepository : ICategoriesRepository
{
public HashSet<string> GetAll() => new HashSet<string>
{
"food", "office", "music"
};
}
in PRogram.cs line 11 I added
builder.Services.AddSingleton<ICategoriesRepository, CategoriesRepository>();
In addition, I updated the custom search binder to use it. Here are the changes:
now when I run the app and call the route http://localhost:5102/odata/Products?$search="office" I get the following error:
The query specified in the URI is not valid. Unable to resolve service for type 'NewQueryOptionIn8.Models.ICategoriesRepository' while attempting to activate 'NewQueryOptionIn8.Extensions.ProductSaleSearchBinder'.
This shows that ODATA is not using the application DI, but it has only its own isolated DI. I could go to the configuration where we have registered the custom search binder and register the categories repository there, however I don't want to do that. Since in a more advanced application, the repository itself is dependent on many more services.
@mahdighorbanpourptw Thanks for your sharing.
Yes, OData has its own DI (aka, Service Provider), You'd better to register the services at the correct place.
Since you don't want to register the categories same as 'ISearchBinder', you can use 'https://github.com/OData/AspNetCoreOData/blob/main/src/Microsoft.AspNetCore.OData/ODataMvcBuilderExtensions.cs#L61' to retrieve the 'ICategoriesRepository' from the application DI and use it within OData DI.
@xuzhg Thanks for the hint. Here is how I did it and it works!
builder.Services.AddSingleton<ICategoriesRepository, CategoriesRepository>();
builder.Services.AddControllers().
AddOData((opt, sp) =>
opt.EnableQueryFeatures()
.AddRouteComponents("odata", ModelBuilder.GetEdmModel(),
services => services
.AddSingleton<ISearchBinder, ProductSaleSearchBinder>()
.AddSingleton(sp.GetRequiredService<ICategoriesRepository>()))
);