#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
оттуда, если это возможно. Большое спасибо за вашу помощь!