1

I am experimenting with loading views from a database, and as suggested in the article one might want to add some caching to prevent hitting the database every time.

ConfigureServices:

services.AddHttpContextAccessor();
services.AddMemoryCache();

services.AddRazorPages()
    .AddRazorRuntimeCompilation(opt =>
    {
        opt.FileProviders.Add(new DatabaseFileProvider(Configuration["AppSettings:SQLConnectionString"]));
    });

DatabaseFileProvider constructor:

private string _connection;

public DatabaseFileProvider(string connection)
{
    _connection = connection;
}

How do I dependency inject an instance of IMemoryCache to the DatabaseFileProvider class?, as one can do with e.g. a singleton:

ConfigureServices:

services.AddSingleton<AppUtils>();

AppUtils constructor:

private static IMemoryCache _cache;

public AppUtils(IMemoryCache cache)
{
    _cache = cache;
}
2
  • Configure the options via DI learn.microsoft.com/en-us/dotnet/core/extensions/… Commented Jun 11, 2021 at 20:02
  • @Nkosi -- If you could provide an answer with a sample how that should look like, where I get both Options and IMemoryCache injected into the DatabaseFileProvider, I would be very grateful, as I can't see how that should be done (I am a really newbie when it comes to ASP.NET Core and its ways to be set up/configured). Commented Jun 11, 2021 at 23:21

1 Answer 1

6
+500

Use DI services to configure MvcRazorRuntimeCompilationOptions directly

Assuming a target provider like

public class DatabaseFileProvider : IFileProvider {
    private string connection;
    private IMemoryCache cache;

    public DatabaseFileProvider(string connection, IMemoryCache cache) {
        this.connection = connection;
        this.cache = cache;
    }

    //...

}

Creating the provider with the aid of the DI services will allow for any registered dependencies to be resolved and explicitly injected using the deferred configuration delegate.

Reference Use DI services to configure options

services.AddHttpContextAccessor();
services.AddMemoryCache();

services
    .AddOptions<MvcRazorRuntimeCompilationOptions>() 
    .Configure<IServiceProvider>((options, sp) => { //<-- Configuration here
        var cs = Configuration["AppSettings:SQLConnectionString"]);
        var provider = ActivatorUtilities.CreateInstance<DatabaseFileProvider>(sp, cs);
        options.FileProviders.Add(provider);
    });

services.AddRazorPages()
    .AddRazorRuntimeCompilation(); //remove configuration delegate here

Configure allows the use of up to five services to configure options, but if a IServiceProvider is injected, the provider can be used in resolve more dependencies if needed.

If that service locator approach is not preferred, the setup can be rearranged to follow a more pure DI design.

services.AddHttpContextAccessor();
services.AddMemoryCache();

service.AddTransient<IFileProvider, DatabaseFileProvider>(sp => {
    var cs = Configuration["AppSettings:SQLConnectionString"]);
    var provider = ActivatorUtilities.CreateInstance<DatabaseFileProvider>(sp, cs);
    return provider;
});

//... register other providers if any

services
    .AddOptions<MvcRazorRuntimeCompilationOptions>() 
    .Configure<IEnumerable<IFileProvider>>((options, providers) => {
        //add all registered providers
        foreach(IFileProvider provider in providers) {
            options.FileProviders.Add(provider);
        }
    });

services.AddRazorPages()
    .AddRazorRuntimeCompilation();
Sign up to request clarification or add additional context in comments.

5 Comments

I have a concrete IFileProvider that has dependencies on scoped services. If I use this solution, I get the error "Cannot resolve scoped service {path to my class} from root provider." How can we get around this?
@ctorx which approach did you use from the solution? The first or the second?
I actually tried both with no luck. I'm essentially needing the IFileProvider implementation to be able to resolve a scoped instance of an Entity Framework DBContext so that I can use it to fetch razor views from a context.
@ctorx Then create a scope via the provider to resolve the context.
None of my razor views compile when I do this, I get errors that basic stuff like System is not found

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.