简体   繁体   English

如何从 ASP.NET Core 中的 Program.cs 访问 IWebHostEnvironment

[英]How to access IWebHostEnvironment from Program.cs in ASP.NET Core

I have ASP.NET Core Razor pages app and I would like to access IWebHostEnvironment in my Program.cs .我有 ASP.NET 核心 Razor 页面应用程序,我想在我的Program.cs中访问IWebHostEnvironment I seed the DB at the beginning of the application, and I need to pass the IWebHostEnvironment to my initializer.我在应用程序开始时为 DB 播种,我需要将IWebHostEnvironment传递给我的初始化程序。 Here is my code:这是我的代码:

Program.cs程序.cs

public class Program
{
    public static void Main(string[] args)
    {
        var host = CreateHostBuilder(args).Build();

        using (var scope = host.Services.CreateScope())
        {
            var services = scope.ServiceProvider;

            try
            {
                SeedData.Initialize(services);
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred seeding the DB.");
            }
        }

        host.Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

SeedData.cs种子数据.cs

    public static class SeedData
    {
        private static IWebHostEnvironment _hostEnvironment;
        public static bool IsInitialized { get; private set; }

        public static void Init(IWebHostEnvironment hostEnvironment)
        {
            if (!IsInitialized)
            {
                _hostEnvironment = hostEnvironment;
                IsInitialized = true;
            }
        }

        public static void Initialize(IServiceProvider serviceProvider)
        {
            //List<string> imageList = GetMovieImages(_hostEnvironment);

            int d = 0;

            using var context = new RazorPagesMovieContext(
                serviceProvider.GetRequiredService<
                    DbContextOptions<RazorPagesMovieContext>>());

            if (context.Movie.Any())
            {
                return;   // DB has been seeded
            }

            var faker = new Faker("en");
            var movieNames = GetMovieNames();
            var genreNames = GetGenresNames();

            foreach(string genreTitle in genreNames)
            {
                context.Genre.Add(new Genre { GenreTitle = genreTitle });
            }

            context.SaveChanges();
            
            foreach(string movieTitle in movieNames)
            {
                context.Movie.Add(
                    new Movie
                    {
                        Title = movieTitle,
                        ReleaseDate = GetRandomDate(),
                        Price = GetRandomPrice(5.5, 30.5),
                        Rating = GetRandomRating(),
                        Description = faker.Lorem.Sentence(20, 100),
                        GenreId = GetRandomGenreId()
                    }
               );
            }

            context.SaveChanges();
        }

Because I have images in wwwroot and I need to get names of of images from there during initializtion.因为我在wwwroot中有图像,我需要在初始化期间从那里获取图像的名称。 I tried to pass IWebHostEnvironment from Startup.cs inside of configure method:我尝试在配置方法中从Startup.cs传递IWebHostEnvironment

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        int d = 0;
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        SeedData.Init(env); // Initialize IWebHostEnvironment
        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
        });
    }

But it seems that the Startup.Configure method gets executed after the Program.Main method.但似乎Startup.Configure方法在Program.Main方法之后执行。 Then I decided to do it in Startup.ConfigureServices method, but it turns out that this method can only take up to 1 parameter.然后我决定在Startup.ConfigureServices方法中进行,但事实证明该方法最多只能占用 1 个参数。 Is there any way to achieve this?有什么办法可以做到这一点? However, I'm not sure that the way I'm trying to seed my data is the best one, I just see this way as the most appropriate for my case, so I would totally appreciate any other suggested approach.但是,我不确定我尝试播种数据的方式是最好的方式,我只是认为这种方式最适合我的情况,所以我非常感谢任何其他建议的方法。

Similar problems I found:我发现的类似问题:

The original issue demonstrates how trying to use DI with static classes cause more problems than it solves.原始问题演示了如何尝试将 DI 与 static 类一起使用会导致比它解决的问题更多的问题。

The seeder can be a scoped registered class and resolved from the host after it has been built.播种器可以是一个作用域注册的 class 并在构建后从主机解析。 The host environment and any other dependencies can be explicitly injected via constructor injection主机环境和任何其他依赖项可以通过构造函数注入显式注入

For example例如

public class SeedData {
    private readonly IWebHostEnvironment hostEnvironment;
    private readonly RazorPagesMovieContext context;
    private readonly ILogger logger;

    public SeedData(IWebHostEnvironment hostEnvironment, RazorPagesMovieContext context, ILogger<SeedData> logger) {
        this.hostEnvironment = hostEnvironment;
        this.context = context;
        this.logger = logger;
    }

    public void Run() {
        try {
            List<string> imageList = GetMovieImages(hostEnvironment); //<<-- USE DEPENDENCY

            int d = 0;

            if (context.Movie.Any()) {
                return;   // DB has been seeded
            }

            var faker = new Faker("en");
            var movieNames = GetMovieNames();
            var genreNames = GetGenresNames();

            foreach(string genreTitle in genreNames) {
                context.Genre.Add(new Genre { GenreTitle = genreTitle });
            }

            context.SaveChanges();
            
            foreach(string movieTitle in movieNames) {
                context.Movie.Add(
                    new Movie {
                        Title = movieTitle,
                        ReleaseDate = GetRandomDate(),
                        Price = GetRandomPrice(5.5, 30.5),
                        Rating = GetRandomRating(),
                        Description = faker.Lorem.Sentence(20, 100),
                        GenreId = GetRandomGenreId()
                    }
               );
            }

            context.SaveChanges();
        } catch (Exception ex) {
           logger.LogError(ex, "An error occurred seeding the DB.");
        }
    }

    // ... other code

}

Note how there was no longer a need for Service Locator anti-pattern.请注意不再需要服务定位器反模式。 All the necessary dependencies are explicitly injected into the class as needed.所有必要的依赖项都根据需要显式注入到 class 中。

Program can then be simplified然后可以简化Program

public class Program {
    public static void Main(string[] args) {
        var host = CreateHostBuilder(args).Build();

        using (var scope = host.Services.CreateScope()) {
            SeedData seeder = scope.ServiceProvider.GetRequiredService<SeedData>();
            seeder.Run();
        }    
        host.Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureServices(services => {
                services.AddScoped<SeedData>(); //<-- NOTE 
            })
            .ConfigureWebHostDefaults(webBuilder => {
                webBuilder.UseStartup<Startup>();
            });
}

where the seeder is registered with the host and resolved as needed before running the host.播种机在主机上注册并在运行主机之前根据需要进行解析。 Now there is no need to access anything other than the seeder.现在不需要访问除了播种机之外的任何东西。 IWebHostEnvironment and all other dependencies will be resolved by the DI container and injected where needed. IWebHostEnvironment和所有其他依赖项将由 DI 容器解析并在需要的地方注入。

The solution for my problem was to simply request IWebHostEnvironment from ServiceProvider.GetRequiredService<T> :我的问题的解决方案是简单地从ServiceProvider.GetRequiredService<T>请求IWebHostEnvironment

Main主要的

var host = CreateHostBuilder(args).Build();

using (var scope = host.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    var hostEnvironment = services.GetRequiredService<IWebHostEnvironment>();

    try
    {
       SeedData.Initialize(services, hostEnvironment);
    }
    catch (Exception ex)
    {
       var logger = services.GetRequiredService<ILogger<Program>>();
       logger.LogError(ex, "An error occurred seeding the DB.");
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM