как получить доступ к IApplicationBuilder в контроллере?

#c# #asp.net-core #dependency-injection #autofac

#c# #asp.net-core #внедрение зависимостей #autofac

Вопрос:

Просто интересно, возможно ли получить доступ к свойствам IApplicationBuilder за пределами startup.cs? Как в контроллере?

Я знаю, что он используется только для определения конвейера приложений, так каким же было бы решение? Что-то вроде регистрации службы, которая упаковывает экземпляр, затем внедряет службу вместо IApplicationBuilder?

Я пытаюсь вернуть свой DbConext из Autofac. Код выглядит следующим образом :

В Business проекте :

  public class AutofacBusinessModule : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterModule(new AutofacDataModule());
        }
    }
  

В проекте данных :

  public class AutofacDataModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<AppDbContext>().InstancePerLifetimeScope();
        }
    }
  

DbContext :

 public class AppDbContext : DbContext
    {
        private const string DbContextName = "AppDbConnectionString";
        public DbSet<Contest> Contests { get; set; }

        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
        {
        }

        public AppDbContext()
        {
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (optionsBuilder.IsConfigured) return;

            var configuration = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json")
                .Build();
            var connectionString = configuration.GetConnectionString(DbContextName);
            optionsBuilder.UseSqlServer(connectionString,
                x => x.MigrationsAssembly("Cri.CodeGenerator.Data"));
        }

        public virtual void Commit()
        {
            base.SaveChanges();
        }
    }
  

И ConfigureServices в startup.cs веб-проекте :

 public IServiceProvider ConfigureServices(IServiceCollection services)
{


    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    var builder = new ContainerBuilder();

   builder.RegisterModule(new AutofacBusinessModule());


    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();

    services.AddScoped(sp => {
        var actionContext = sp.GetRequiredService<IActionContextAccessor>().ActionContext;
        var urlHelperFactory = sp.GetRequiredService<IUrlHelperFactory>();
        var urlHelper = urlHelperFactory.GetUrlHelper(actionContext);
        return urlHelper;
    });

    services.AddDistributedMemoryCache();
    builder.Populate(services);

    ApplicationContainer = builder.Build();

    //return the IServiceProvider implementation
    return new AutofacServiceProvider(ApplicationContainer);

}
  

Я, конечно, чего-то не понимаю, но действительно новичок, когда дело доходит до DI и .net core…

— РЕДАКТИРОВАТЬ —

В моем Controller

   private readonly IHostingEnvironment _hostingEnvironment;
        private readonly IApplicationBuilder _app;


        private const int NumberOfCharactersRepetion = 4;

        public UniqueCodesController(IHostingEnvironment hostingEnvironment, IApplicationBuilder app)
        {
            _hostingEnvironment = hostingEnvironment;
            _app = app;
        }
  

 if (selectedAnswer == FileExtension.XLSX.GetStringValue())
                {
                    await FilesGenerationUtils.GenerateExcelFile(_app, uniqueCodesHashSet, model);
                }
  

в GenerateExcelFile методе :

 public static async Task GenerateExcelFile(IApplicationBuilder app, IEnumerable<string> hashSetCodes, ContestViewModel model)
        {
...

 try
                    {
                        var context = app.ApplicationServices.GetRequiredService<AppDbContext>();

                        var contest = new Contest
                        {
                            Name = model.ProjectName,
                            UserId = 1,
                            FileGenerationStatus = true,
                            FilePath = fileInfo.FullName
                        };
                        context.Contests.Add(contest);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }

}
  

Но когда я запускаю приложение, я получаю это сообщение :

 InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Builder.IApplicationBuilder' while attempting to activate 'Cri.CodeGenerator.Web.Controllers.UniqueCodesController'.
  

Комментарии:

1. Обычно, когда у вас что-то зарегистрировано в DI, вы добавляете это в качестве параметра в конструктор, поэтому в данном случае это конструктор вашего контроллера.

2. Обычно вы помещаете зависимость в конструктор контроллера и позволяете контейнеру DI разрешить ее. Это не работает? Как вы думаете, где именно вам нужен IApplicationBuilder? Вы этого не показали, поэтому немного сложно понять проблему. Если вы пробовали это, и это не сработало, расскажите подробнее об этом, например, было ли исключение или какое-либо другое неожиданное поведение? Все, что у вас здесь есть, на первый взгляд выглядит разумно.

3. @MikeZboray Я только что обновил исходное сообщение, чтобы показать, что я пытаюсь сделать и что я получаю при попытке. Спасибо за ваше время 🙂

Ответ №1:

НЕТ, это невозможно, насколько я знаю.

Вам придется написать промежуточное программное обеспечение (или какой-либо другой код), чтобы получить данные из IApplicationBuilder в экземпляр HttpContext.

Тогда к экземпляру HttpContext вы можете получить доступ изнутри контроллеров.

Надеюсь, это поможет.

Ответ №2:

Звучит так, как будто вы пытаетесь получить новый экземпляр AppDbContext .

Если вам нужно сохранить GenerateExcelFile() как static и вы хотите повторно использовать AppDbContext через параметр, вы могли бы заставить его принимать экземпляр AppDbContext вместо IApplicationBuilder .

Во-первых, просто внедрите такой экземпляр службы напрямую:

     private readonly IHostingEnvironment _hostingEnvironment;
    private readonly AppDbContext _dbContext;

    // ...

    public UniqueCodesController(IHostingEnvironment hostingEnvironment, AppDbContext dbContext)
    {
        _hostingEnvironment = hostingEnvironment;
        _dbContext = dbContext;
    }
  

А затем измените GenerateExcelFile() , чтобы принять параметр AppDbContext

    общедоступный статический асинхронный файл GenerateExcelFile задачи (приложение IApplicationBuilder, IEnumerable<string>hashSetCodes, модель ContestViewModel)  общедоступный статический асинхронный файл GenerateExcelFile задачи (AppDbContext DbContext, IEnumerable hashSetCodes, модель ContestViewModel) { ... попробуйте{  var context = приложение.Службы приложений.GetRequiredService();  var contest = новый конкурс { Имя = модель.Имяпроекта, идентификатор пользователя = 1, FileGenerationStatus = true, путь к файлу = FileInfo.Полное имя };  контекст.Конкурсы.Добавить (конкурс);
 контекст.Конкурсы.Добавить (конкурс);
 }
 перехват (исключение ex)
 {
 Консоль.Строка записи (например, сообщение);
 }

 }

Наконец, вы могли бы вызвать его, как показано ниже :

     await FilesGenerationUtils.GenerateExcelFile(_dbContext, uniqueCodesHashSet, model);
  

В качестве дополнительного примечания, если вы не можете определить требуемый тип во время компиляции и хотите динамически разрешить некоторый тип сервиса, вы могли бы ввести IServiceProvider вместо IApplicationBuilder . Таким образом, вы могли бы разрешить любой экземпляр по своему усмотрению :

     var dbContext= sp.GetRequiredService<AppDbContext>();
    // or if you need a service only available within a scope 
    using(var scope = this._sp.CreateScope()){
        var _anotherDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        ...
    }
  

Взяв ваш код в качестве примера, вы могли бы передать IServiceProvider в GenerateExcelFile(IServiceProvider sp, IEnumerable<string> hashSetCodes, ContestViewModel model) , и в рамках GenerateExcelFile() метода вы могли бы разрешить AppDbContext проблему следующим образом:

     public static async Task GenerateExcelFile(IServiceProvider sp, IEnumerable<string> hashSetCodes, ContestViewModel model)
    {
        ...

        var dbContext= sp.GetRequiredService<AppDbContext>();

        try{
            var contest = new Contest
            {
                Name = model.ProjectName,
                UserId = 1,
                FileGenerationStatus = true,
                FilePath = fileInfo.FullName
            };

            dbContext.Contests.Add(contest);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }

    }
  

Комментарии:

1. Вау, это хорошо продуманный ответ, большое спасибо за это! Я пытался внедрить, IServiceProvider как вы упомянули, для решения AppDbContext но когда дело доходит до dbContext.Contests.Add(contest); , я получаю это System.IO.FileNotFoundException: The configuration file 'appsettings.json' was not found and is not optional. The physical path is 'C:Program FilesIIS Expressappsettings.json'. … Есть какие-нибудь подсказки?

2. @j0w Я заметил, что вы предполагаете, что appsettings.json находится в текущем каталоге при запуске в IIS. По какой-то причине ваш текущий каталог 'C:Program FilesIIS Express . Я не думаю, что там будет C:Program FilesIIS Expressappsettings.json файл. В результате он выдает ошибку. Без кода я не уверен, почему ваш текущий каталог изменен на C:Program FilesIIS Express . Однако его можно использовать Assembly.GetExecutingAssembly().Location для получения базового местоположения.

3. Просто выясните, что я запускал проект на IIS вместо web project , и именно поэтому каталог не был подходящим. Сейчас я просто запущу веб-проект, а позже попытаюсь использовать laucj с IIS, чтобы вернуть appsettings.json оттуда, если это возможно. Большое спасибо за вашу помощь!