问题
I want to create a web service with ASP.NET Core 2.1 which checks on application startup if the connection to the database works and then prepares some data in the database.
The check runs in a loop till the connection was successful or the users presses Ctrl + C (IApplicationLifetime
). It is important that no HTTP call is processed before the database was initialized. My question is: where to put this code?
I need a the dependency injection system to be fully initialized, so the earliest i can think of would be at the end of my Startup.Configure
method, but the cancellation tokens on IApplicationLifetime
do not seem to work there (properly because asp isn't fully started)
Is there a official place where can put this startup logic?
回答1:
You can build an extension method off of IWebHost
which will allow you to run code before Startup.cs
. Furthermore, you can use the ServiceScopeFactory
to initialize any services you have (e.g. 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;
}
Then you can consume the method.
public static void Main(string[] args)
{
BuildWebHost(args)
.CheckDatabase()
.Run();
}
回答2:
where to put this code?
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();
}
}
is there a official place where can put this startup logic?
Startup.cs is good place to start...
Initializer.WaitOnAction(()=> /* ensure db is initialized here */);
/* check https://dotnetfiddle.net/gfTyTL */
回答3:
I want to create a web service with ASP.NET Core 2.1 which checks on application startup
So for example I had a scenario to check for the folder structure if not create one as soon as the application starts.
The method to create folder structure is in FileService.cs which has to be initiated via DI as soon as the application starts up before any http request. appsettings.json conatins keys and values which contains the structure to create folder structure.
"FolderStructure": {
"Download": {
"English": {
"*": "*"
},
"Hindi": {
"*": "*"
}
},
"Upload": {
"*": "*"
}
}
And used below interface and service
Interface
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);
}
}
Service
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);
}
}
}
Now the challenge is to call the folder service method as soon as the application starts but we need to initialize fileservice via DI
services.AddSingleton<IFileService, FileService>();
And in Configure method you can call the required service.
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();
}
来源:https://stackoverflow.com/questions/52786605/where-to-put-application-startup-logic-in-asp-net-core