7

I have implemented the EntityFrameworkFileProvider for my ASP.NET core web application, I want the ViewDbContext instance to be injected by ASP.NET core DI framework in the constructor:

(ViewDbContext is a dbContext)

public class EntityFrameworkFileProvider : IFileProvider
{
    private ViewDbContext _context;

    public EntityFrameworkFileProvider(ViewDbContext context)
    { 
       /* should be injected by asp.net core DI */
        _context = context;
    }
    public IDirectoryContents GetDirectoryContents(string subpath)
    {
        .....
    }

    public IFileInfo GetFileInfo(string subpath)
    {
        var result = new DatabaseFileInfo(_context, subpath);
        return result.Exists ? result as IFileInfo : new NotFoundFileInfo(subpath);
    }

    public IChangeToken Watch(string filter)
    {
        return new DatabaseChangeToken(_context, filter);
    }
}

Now I add the EntityFrameworkFileProvider to RazorViewEngineOption in startup.cs How to make the ViewDbContext instance to be automatically injected by DI framework in the ConfigureServices method of startup.cs? how should i call the EntityFrameworkFileProvider constructor correctly?

In Startup.cs

public void ConfigureServices(IServiceCollection services)
{
      /* Add  EntityFrameworkFileProvider to Razor engine */
      services.Configure<RazorViewEngineOptions>(opts =>
      {
          opts.FileProviders.Add(new EntityFrameworkFileProvider(null?));
      });

      services.AddMvc();
}
Søren
  • 6,517
  • 6
  • 43
  • 47
dinner689
  • 381
  • 1
  • 4
  • 10

2 Answers2

21

i think i have found the solution! any idea?

public void ConfigureServices(IServiceCollection services)
{
     services.AddDbContext<ViewDbContext>(options => 
       options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    ...
     /* Add  EntityFrameworkFileProvider to Razor engine */       
     var context = services.BuildServiceProvider()
                       .GetService<ViewDbContext>();

     services.Configure<RazorViewEngineOptions>(opts =>
     {
         opts.FileProviders.Add(new EntityFrameworkFileProvider(context));
     });

     services.AddMvc();
}
dinner689
  • 381
  • 1
  • 4
  • 10
  • 1
    Using your approach, doesn't it mean the same context object is used again and again... each time the framework wants to retrieve file details or check if a file has changed. This could effectively give you a memory leak if the context is tracking objects. The samples I have seen use a new DB Context for each call. – Piers Lawson May 07 '17 at 14:18
  • Piers Lawson: what do you mean, can you provide example please ? – Muflix May 05 '19 at 09:46
  • @PiersLawson I don't think the context is storing a singleton of the ViewDbContext. It would probably instantiate based on the existing configuration for that type, i.e. AddDbContext on line 3 of the snippet above. – Bon Aug 11 '19 at 14:51
  • 2
    warning, BuildServiceProvider() results in additional copies of all Singleton services being created. – Tom McDonough Dec 05 '19 at 11:27
  • I don't know how much of a difference this makes, but I am required to build an Angular front end to this app. Razor won't be involved unless I can hide it some place. – EoRaptor013 Jun 08 '21 at 21:58
  • what if he used the ServiceProvider as Static instance and used the using keyword? using (_Provider = _Provider ?? services.BuildServiceProvider())using (var context = _Provider.GetService()) – PontiacGTX Nov 18 '22 at 17:52
5

A better approach would be to inject the context in Configure method and use it it there

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ViewDbContext context)
{
     services.AddDbContext<ViewDbContext>(options => 
       options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    ...
     /* Add  EntityFrameworkFileProvider to Razor engine */       
     /* Use db context here*/

     services.Configure<RazorViewEngineOptions>(opts =>
     {
         opts.FileProviders.Add(new EntityFrameworkFileProvider(context));
     });

     services.AddMvc();
}
  • 2
    I don't get this. Services are added in the ConfigureServices function, not the Configure function. Is this a typo, or is something going on I don't understand. – EoRaptor013 May 10 '21 at 04:41
  • 1
    @EoRaptor013 Add `private IServiceCollection _services;` to the top of `Startup`, assign it `_services = services;` in `ConfigureServices(IServiceCollection services){ ... }`, then call `_services` as you like from `Configure`. – J.D. Mallen Jun 08 '21 at 13:58