Closed mchender closed 6 years ago
@danroth27 @DamianEdwards thoughts?
@Eilon @pranavkm @rynowak
For the issue that @mchender is running in to, IConfigureOptions
would be a better way to get to it rather than using the static field:
public class CustomFileProviderOptions : IConfigureOptions<RazorViewEngineOptions>
{
private IFileProvider _fileProvider;
public CustomFileProviderOptions(IMyApplicationFileProvider fileProvider)
{
_fileProvider = fileProvider;
}
public void Configure(RazorViewEngineOptions options)
{
options.FileProviders.Clear();
options.FileProviders.Add(_fileProvider);
}
}
services.AddMvc();
services.AddTransient<IConfigureOptions<RazorViewEngineOptions>, CustomFileProviderOptions>();
services.AddSingleton<IMyApplicationFileProvider, CacheFileProvider>(); // Use a custom marker interface so you aren't conflating it with random IFileProvider instances in the DI
If we do make this a first class feature (and I'm not entirely convinced we should), it would be consistent to ensure Hosting IHostingEnvironment.ContentRootFileProvider \ WebRootFileProvider
and StaticFiles also support this.
It would be amazing to have Configure<TOptions, TArg1>(Action<TOptions, TArg1>)
where TArg1
is resolved from services. Basically the code that @pranavkm provided except the Configure
method calls your delegate and is provided by the options system.
We have to write these classes like this all of the time in MVC and it's tedious.
/cc @davidfowl is this something that was discuussed?
It's not a bad idea but its noisy because you have to specify all generic arguments. @HaoK recently made registering IConfigureOptions
a bit nicer in 2.1. You can do this now:
Old:
services.AddTransient<IConfigureOptions<RazorViewEngineOptions>, CustomFileProviderOptions>();
New:
services. ConfigureOptions<CustomFileProviderOptions>();
@pranavkm that provides a workaround for the question in this post. Is there some equivalent workaround which would allow me to get around having to pass an instance of my file provider when configuring the hosting environment?
IFileProvider fileProvider = new CacheFileProvider();
services.AddSingleton<IHostingEnvironment>(new HostingEnvironment
{
ApplicationName = applicationEnvironment.ApplicationName,
WebRootFileProvider = fileProvider,
});
It's not a bad idea but its noisy because you have to specify all generic arguments.
But you still have to write the class:
public class CustomFileProviderOptions : IConfigureOptions<RazorViewEngineOptions>
{
private IFileProvider _fileProvider;
public CustomFileProviderOptions(IMyApplicationFileProvider fileProvider)
{
_fileProvider = fileProvider;
}
public void Configure(RazorViewEngineOptions options)
{
options.FileProviders.Clear();
options.FileProviders.Add(_fileProvider);
}
}
5/7 loc of that are boilerplate. Feels bad man
services.Configure<RazorViewEngineOptions, IMyApplicationFileProvider>((options, provider) =>
{
options.FileProviders.Clear();
options.FileProviders.Add(_fileProvider);
});
VS
services.ConfigureOptions<CustomFileProviderOptions>();
...
public class CustomFileProviderOptions : IConfigureOptions<RazorViewEngineOptions>
{
private IFileProvider _fileProvider;
public CustomFileProviderOptions(IMyApplicationFileProvider fileProvider)
{
_fileProvider = fileProvider;
}
public void Configure(RazorViewEngineOptions options)
{
options.FileProviders.Clear();
options.FileProviders.Add(_fileProvider);
}
}
Still not sure why this discussion is in this repo...
@Tratcher based on this comment https://github.com/aspnet/EntityFrameworkCore/issues/4544#issuecomment-184794032
@pranavkm you can ignore my follow up question. It looks like I don't have to set WebRootFileProvider on the HostingEnvironment for the application to work properly. I made the changes you suggested and I was able to get DI to resolve the services I need in CacheFileInfo. Here's the commit. https://github.com/mchender/Entropy/commit/7405b5965d86c793f81b05c44908a611a4813b6a
My issues are resolved. Feel free to close this issue if needed. Otherwise I am interested in the result of the conversation between @rynowak and @davidfowl and how to either make this a first class feature or at least make how it's configured as intuitive as possible.
Ok, we could add a new generic base configure, maybe something like:
services.ConfigureFromDI<TOptions, TDependency>(Action<TOptions> configure)
which would register as transient something like:
class ConfigureFromDI<TOptions, TDependency> : IConfigureOptions<TOptions> {
ConfigureWithDI(TDependency dep) => Dep = dep;
TDependency Dep { get; }
}
If the configure options needs more than one dependency, to use this sugar you'd have to wrap them and register it and get them out as properties?
services.AddScoped<MyOptionsDep>();
class MyOptionsDep {
MyOptionsDep(MyService1 s, MyService2, s2);
MyService1 Service1 { get; }
MyService2 Service1 { get; }
}
Leave out the FromDI part it would just be configure with a bunch of overloads for dependencies. We can start with 5 dependencies 😄
Ok so just have 5 overloads, with 1 to 5 extra args, and they will just roll their own otherwise. I'll file an issue
This issue was moved to aspnet/Home#2450
I'd like to be able to retrieve an instance of my file provider from DI similar to how ServiceFilterAttribute works. https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters#servicefilterattribute
This was discussed in another thread in Entity Framework but since they found a workaround I don't know that anyone followed up submitting the issue here. https://github.com/aspnet/EntityFramework/issues/4544#issuecomment-184794032
My current workaround was to follow the guidance provided in the following comment which stores a reference to IServiceProvider in a static property. However, they indicate it is a bad practice that should be avoided if possible. https://github.com/aspnet/DependencyInjection/issues/294#issuecomment-142206501
My true goal was to develop a library that can render a MVC Razor view to a string. There is an example of how to do this in the Entropy repository. However, it requires the view to exist on disk. https://github.com/aspnet/Entropy/tree/dev/samples/Mvc.RenderViewToString
My templates will come from a database so I need to be able to pass the view as a string and get the rendered output as a string. I updated the sample from Entropy by adding a custom implementation of IFileProvider that will load the the template from the cache. When I pass in the string from the database it adds the template to the cache using a randomly generated guid as the key. When I request the view from the IRazorViewEngine I use that guid as the file name so that later I can use it as the key to retrieve the template from the cache. https://github.com/mchender/Entropy/tree/dev/samples/Mvc.RenderViewToString
Snippet from https://github.com/mchender/Entropy/blob/dev/samples/Mvc.RenderViewToString/Program.cs
Snippet from https://github.com/mchender/Entropy/blob/dev/samples/Mvc.RenderViewToString/RazorViewToStringRenderer.cs
Snippet from https://github.com/mchender/Entropy/blob/dev/samples/Mvc.RenderViewToString/CacheFileInfo.cs
It would be much cleaner if IMemoryCache could be loaded using DI instead of having to use a static property to access IServiceProvider to request the service myself.