I'm still very new to .NET 7 (coming from .NET Framework) and its out-of-the-box DI system so forgive my ignorance here.
I'm trying to write a custom ModelMetadataDetailsProvider
in my ASP.NET project so that I can get some details from the database when models are being bound and cache the data to reduce database calls. I have this in my provider:
public class MyDisplayMetadataProvider : IDisplayMetadataProvider
{
private readonly MyDBContext _db;
private readonly IMemoryCache _memoryCache;
public MyDisplayMetadataProvider(MyDBContext db, IMemoryCache memoryCache)
{
_db = db;
_memoryCache = memoryCache;
}
public void CreateDisplayMetadata(DisplayMetadataProviderContext context)
{
// etc.
I'm trying to wire up this provider in my Program.cs file but that's where I'm getting stuck. When I was just getting started with this provider, before I added the parameters in the constructor, this worked fine:
builder.Services.AddControllersWithViews(options =>
{
options.ModelMetadataDetailsProviders.Add(new MyDisplayMetadataProvider());
});
builder.Services.AddDbContext<MyDBContext>(options => options.UseSqlServer("name=ConnectionStrings:DefaultConnection"));
However, now that my provider's constructor requires two parameters, Intellisense understandably points out that I haven't provided the required parameters for the database context and memory cache. I know I'm doing something wrong because I wouldn't expect to be manually instantiating an instance of the class with new
when using DI, but I cannot for the life of me figure out what the syntax should be and I've spent literally hours searching SO and Google for clues, with no success.
Copying the syntax I've used successfully elsewhere to register a Filter, I tried this:
builder.Services.AddControllersWithViews(options =>
{
options.ModelMetadataDetailsProviders.Add<MyDisplayMetadataProvider>();
});
builder.Services.AddDbContext<MyDBContext>(options => options.UseSqlServer("name=ConnectionStrings:DefaultConnection"));
but that causes this error to be displayed:
There is no argument given that corresponds to the required parameter 'configureSource' of 'ConfigurationExtensions.Add(IConfigurationBuilder, Action?)'
which really is confusing to me. I expected that Googling that one would yield lots of useful suggestions from people who'd encountered the same error, but in fact there don't seem to be any exact matches at all.
How can I wire up my custom provider using DI or, if that's not possible (I'm sure it is!), how can I supply the parameters it requires?
Update
Following the answer from Guru Stron I have created an additional method which implement IConfigureOptions
. I'm just having a bit of trouble getting the registration part working with the database context. Following the advice from the answer they linked to, I've tried this:
builder.Services.AddControllersWithViews();
builder.Services.AddDbContext<MyDBContext>(options => options.UseSqlServer("name=ConnectionStrings:DefaultConnection"));
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<MyDBContext>();
builder.Services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, MyDisplayMetadataProviderSetup>());
}
This doesn't work, as I knew it wouldn't, because the service collection is read-only at the point I'm calling TryAddEnumerable
, since I've already called builder.Build()
at that point.
But if I move the using
block to above the var app = builder.Build()
line then I can't use app.Services.CreateScope()
because the app
variable hasn't been declared at that point.
I think I've nearly cracked it, but I just can't quite figure out the registration part with the scoped db context.