【问题标题】:Where to put application startup logic in ASP.NET CoreASP.NET Core 中应用程序启动逻辑的放置位置
【发布时间】:2019-03-18 02:28:13
【问题描述】:

我想使用 ASP.NET Core 2.1 创建一个 Web 服务,它会检查应用程序启动时与数据库的连接是否正常,然后在数据库中准备一些数据。

检查循环运行,直到连接成功或用户按下 Ctrl + C (IApplicationLifetime)。重要的是在初始化数据库之前不处理任何 HTTP 调用。我的问题是:把这段代码放在哪里?

我需要一个完全初始化的依赖注入系统,所以我能想到的最早是在我的Startup.Configure 方法的末尾,但是IApplicationLifetime 上的取消标记似乎在那里不起作用(正确因为asp没有完全启动)

有官方的地方可以放这个启动逻辑吗?

【问题讨论】:

  • 您将在这里面临的问题是您的应用程序在收到 HTTP 连接之前不会“启动”,因此您的“不处理 HTTP 调用”的概念就在窗外。它已经处于 HTTP 连接的中间。我建议您也许应该运行一个 Windows 服务来准备数据库,然后在一切启动并运行后启用该网站。
  • “我想用 ASP.NET Core 2.1 创建一个 Web 服务,它会检查应用程序的启动情况......”你所指的应用程序是否是 Web 服务尚不完全清楚?虽然这是假设。
  • 下面的docs.microsoft.com/en-us/dotnet/architecture/microservices/…描述了一些实现IHostIHostedService的选项,例如

标签: c# asp.net-core .net-core startup


【解决方案1】:

您可以从IWebHost 构建一个扩展方法,这将允许您在Startup.cs 之前运行代码。此外,您可以使用ServiceScopeFactory 来初始化您拥有的任何服务(例如DbContext)。

public static IWebHost CheckDatabase(this IWebHost webHost)
{
    var serviceScopeFactory = (IServiceScopeFactory)webHost.Services.GetService(typeof(IServiceScopeFactory));

    using (var scope = serviceScopeFactory.CreateScope())
    {
        var services = scope.ServiceProvider;
        var dbContext = services.GetRequiredService<YourDbContext>();

        while(true)
        {
            if(dbContext.Database.Exists())
            {
                break;
            }
        }
    }

    return webHost;
}

然后你就可以使用这个方法了。

public static void Main(string[] args)
{
    BuildWebHost(args)
        .CheckDatabase()
        .Run();
}

【讨论】:

    【解决方案2】:

    把这段代码放在哪里?

    class Initializer
    {
        internal static SemaphoreSlim _semaphoreSlim;
        static SemaphoreSlim Slim
        {
            get
            {
                return LazyInitializer.EnsureInitialized(ref _semaphoreSlim, () => new SemaphoreSlim(1, 1));
            }
        }
    
        public static void WaitOnAction(Action initializer)
        {
            Initializer.Slim.Wait();
            initializer();
            Initializer.Slim.Release();
        }
    }
    

    有官方的地方可以放这个启动逻辑吗?

    Startup.cs 是开始的好地方...

    Initializer.WaitOnAction(()=> /* ensure db is initialized here */); 
    /* check https://dotnetfiddle.net/gfTyTL */
    

    【讨论】:

    【解决方案3】:

    我想使用 ASP.NET Core 2.1 创建一个 Web 服务,用于检查应用程序启动

    因此,例如,我有一个场景来检查文件夹结构,如果在应用程序启动时不创建文件夹结构。

    创建文件夹结构的方法在 FileService.cs 中,应用程序在任何 http 请求之前启动时,必须通过 DI 启动该方法。 appsettings.json 包含键和值,其中包含创建文件夹结构的结构。

    "FolderStructure": {
        "Download": {
          "English": {
            "*": "*"
          },
          "Hindi": {
            "*": "*"
          }
        },
        "Upload": {
          "*": "*"
        }
      }
    

    并在接口和服务下面使用

    界面

    namespace MyApp.Services
    {
        public interface IFileService
        {
            void CreateDirectoryStructure(string path = "");
            void CreateFolder(string name, string path = "");
            void CreateFile(string name, string path = "");
            bool CheckFileExists(string path);
            bool CheckFolderExists(string path); 
        }
    }
    

    服务

    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.FileProviders;
    using Microsoft.Extensions.Configuration.Binder;
    using System.IO;
    using Microsoft.Extensions.Logging;
    
    namespace MyApp.Services
    {
        public class FileService : IFileService
        {
            private readonly IFileProvider _fileProvider;
            private readonly IHostingEnvironment _hostingEnvironment;
            private readonly IConfiguration _config;
            private readonly ILogger<FileService> _logger;
            string defaultfolderPath = ConfigurationManager.AppSetting["DefaultDrivePath"];
            public FileService(IFileProvider fileProvider, IHostingEnvironment hostingEnvironment, IConfiguration config, ILogger<FileService> logger)
            {
                _fileProvider = fileProvider;
                _hostingEnvironment = hostingEnvironment;
                _config = config;
                _logger = logger;
            }
            public void CreateDirectoryStructure(string drivePath = "")
            {     
                if (drivePath.Equals(""))
                {
                    drivePath = ConfigurationManager.AppSetting["DefaultDrivePath"];
                    _logger.LogInformation($"Default folder path will be picked {drivePath}");
                }
                foreach (var item in _config.GetSection("FolderStructure").GetChildren())
                {
                    CreateFolder(item.Key, drivePath);
                    foreach (var i in _config.GetSection(item.Path).GetChildren())
                    {
                        if (i.Key != "*")
                            CreateFolder(i.Key, $"{drivePath}/{item.Key}");
                    }
                }
            }
            public void CreateFolder(string name, string path = "")
            {
                string fullPath = string.IsNullOrEmpty(path) ? $"{defaultfolderPath}/{name}" : $"{path}/{name}";
                if (!Directory.Exists(fullPath))
                {
                    Directory.CreateDirectory(fullPath);
                    _logger.LogInformation($"Directory created at {fullPath} on {DateTime.Now}");
                }
            }
            public void CreateFile(string name, string path = "")
            {
                string fullPath = string.IsNullOrEmpty(path) ? $"{defaultfolderPath}/{name}" : $"{path}/{name}";
                if (!File.Exists(fullPath))
                {
                    File.Create(fullPath);
                    _logger.LogInformation($"File created at {fullPath} on {DateTime.Now}");
                }
            }
            public bool CheckFolderExists(string path)
            {
                string fullPath = string.IsNullOrEmpty(path) ? defaultfolderPath : path;
                return Directory.Exists(fullPath);
            }
    
            public bool CheckFileExists(string path)
            {
                string fullPath = string.IsNullOrEmpty(path) ? defaultfolderPath : path;
                return File.Exists(fullPath);
            }
    
        }
    }
    

    现在的挑战是在应用程序启动后立即调用文件夹服务方法,但我们需要通过 DI 初始化文件服务

      services.AddSingleton<IFileService, FileService>();
    

    并且在配置方法中你可以调用所需的服务。

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, IFileService FileService)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseHsts();
            }
            //dont change the below order as middleware exception need to be registered before UseMvc method register
            app.ConfigureCustomMiddleware();
            // app.UseHttpsRedirection();
            app.UseMvc();
            FileService.CreateDirectoryStructure();
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-22
      • 1970-01-01
      • 1970-01-01
      • 2012-09-12
      • 2013-01-10
      • 1970-01-01
      相关资源
      最近更新 更多