.NET 6 how to run Migration automatically in program.cs - asp.net-core

In .Net 5, we use to be able to call the migration by passing DataContext to Configure method and call the migration in startup class.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DataContext dataContext)
{
// migrate any database changes on startup (includes initial db creation)
dataContext.Database.Migrate();
...
}
How can we do it in .Net 6?

Short Version
It sounds like the real question is where to put code that used to live in Startup.Configure.
In Program.cs use
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
db.Database.Migrate();
}
Rather long explanation
The Applying Migrations at Runtime section in the EF Core Migrations docs shows that nothing's changed as far as EF Core is concerned.
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
//Same as the question
db.Database.Migrate();
}
host.Run();
}
It sounds like the real question is where to put code that used to live in Startup.Configure. That code can be placed in the Main method or, if Minimal APIs are used, inside Program.cs. Configuration, Services, Environment etc are available as properties in the WebApplicationBuilder class or the WebApplication created by it. WebApplicationBuilder contains the builder interfaces for DI, configuration, Logging and the host, eg WebApplicationBuilder.Services exposes IServiceCollection.
WebApplication properties expose the middleware configured by WebApplicationBuilder, eg WebApplication.Services exposes IServiceProvider
Startup replacement in Minimal APIs
The methods that were in Startup.cswere merged in Program.cs in .NET 6. Startup.cs contained two kinds of methods:
Methods to configure the host and application, like setting up configuration and DI, by calling the various builder interfaces like IServiceCollection, IConfigurationBuilder. This includes the code that used to be in Startup.ConfigureServices.
Methods that used the host to configure endpoints, use services and middleware. This includes code that was in Startup.Configure.
In .NET 6, the interfaces move to the WebApplicationBuilder and WebApplication classes. Instead of .NET Core calling a "magic" Startup class and injecting the interfaces, the code in Program.cs can access the interfaces it needs directly.
The host building/configuration services are now available through the WebApplicationBuilder class.
Interfaces provided by the complete application host are now available through the WebApplication class which is built by the WebApplicationBuilder.
If you don't need to configure services, you can create a minimal API application with just 3 lines :
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
In your case you need to configure the DbContext at least, so you need to use WebApplicationBuilder and WebApplication separately. This is shown in the next section
Migrations in Minimal APIs
In the basic minimal API Program.cs :
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
DbContexts can be created once a WebApplication instance is created through its Services property:
var builder = WebApplication.CreateBuilder(args);
//Register the DbContexts etc.
...
builder.Services.AddDbContext<SomeDbContext>(....);
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
db.Database.Migrate();
}
app.MapGet("/", () => "Hello World!");
app.Run();
Of course it's a lot better to use separate methods or classes for such code, keeping Program.cs clean :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<SomeDbContext>(....);
var app = builder.Build();
ApplyMigrations(app);
app.MapGet("/", () => "Hello World!");
app.Run();
static void ApplyMigrations(WebApplication app)
{
using var scope = app.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
db.Database.Migrate();
}
Or even :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<SomeDbContext>(....);
var app = builder.Build();
app.ApplyMigrations()
.UseCustomLogging()
.DoSomeOtherConfiguration()
...;
app.MapGet("/", () => "Hello World!");
app.Run();
With ApplyMigrations an extension method in a separate class :
public static DataExtensions
{
public static WebApplication ApplyMigrations(this WebApplication app)
{
using var scope = app.Services.CreateScope()
var db = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
db.Database.Migrate();
return app;
}
}

In ASP.NET Core 6, it should be:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<YourDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("YourConnectionString")));
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<YourDbContext>();
db.Database.Migrate();
}

Related

Integration tests api in .net6.0

SO when I did integration tests for api's, I used tio have a xunit project and used Microsoft.AspNetcore.Mvc.Testing.
There I used a WebApplicationFactory<namespace.Startup>().
According to microsoft docs they provide roughly the same:
// Arrange
_server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>());
_client = _server.CreateClient();
found here: api integration tests
However since .net6.0 came out when creating an api project and other projects aswell, they don't seem to have a startup class anymore, all is embedded in the program.cs file, But program.cs file doesn't contain a class either, so i'm a little bit stuck on what to use in my webapplicationfactory<namesapce.startup> -> since there is no startup anymore
Any idea what to do here?
Edit:
program.cs of my api (with controllers)
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
public partial class Program { }
my tests:
public class ApiTests
{
private readonly HttpClient client;
private readonly TestServer _server;
private const string url = "https://localhost/api/network";
public ApiTests()
{
_server = new TestServer(WebApplicationFactory<Network_Monitor_Agent.Program>);
}
[Fact]
public async Task GetRequest_Should_Return_Success_Information()
{
var response = await client.GetAsync(url);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}

Database AutoMigration in .NET 6

I'm rewriting a simple application that uses Asp.NET 5. but now I'm using Asp.NET 6 for the new version of my app. Quick question: what's the replacement of database auto migration (like below in .NET5) in .NET6 or What approach should I use for automatic migration after application launch in .NET6?
Sample in .NET5:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, PgSqlDbContext context)
{
context.Database.Migrate();
/*
other middlewares
*/
}
.NET6 :
var app = builder.Build();
// ???? for auto migration
// other middlewares
I have tried to add this code and the project works successfully.
var app = builder.Build();
using (var serviceScope = app.Services.CreateScope())
{
var services = serviceScope.ServiceProvider;
var dbcontext = services.GetRequiredService<PgSqlDbContext>();
var conn = dbcontext.Database.GetConnectionString();
}
//other middlewares
Here's my translation from Microsoft's Docs on 5 to 6 migration to your use case:
var app = builder.Build();
var context = app.Services.GetRequiredService<PgSqlDbContext>();
context.Database.Migrate();
I did not test this, so fingers crossed!

How to conditionally disable Application Insights in ASP.NET Core?

I'm trying to avoid the service locator pattern in ASP.NET Core when conditionally including Application Insights in my ASP.NET Core application, the reason for this is I want to completely disable Applications Insights during development.
Service locator pattern (not recommended)
The most basic way of doing this is to service locate a IOptions setting in ConfigureServices() after building a service provider using the BuildServiceProvider() method on the IServiceCollection
Example of (not recommended!) service locator pattern:
public void ConfigureService(IServiceCollection services)
{
// Configure the services
services.Configure<AppSettings>(configuration.GetSection(nameof(AppSettings)));
// Build an intermediate service provider
var sp = services.BuildServiceProvider();
// Resolve the settings from the service provider;
var appSettings = sp.GetRequiredService<AppSettings>();
// Conditionally include the service using the settings
if (appSettings.EnableApplicationInsights) {
services.AddApplicationInsightsTelemetry();
}
}
This is not a recommended pattern as it results in an additional copy of singleton services being created. But we can be sure Application Insights is completely disabled in the application, in fact it's not even included in the DI container.
Better pattern #1
A much better way of resolving classes that are dependent on other services is to use the AddXXX overload that provides you with the IServiceProvider. This way you do not need to instantiate an intermediate service provider.
The following samples show how you can use this overload in AddSingleton/AddTransient methods.
services.AddSingleton(serviceProvider =>
{
var settings = serviceProvider.GetRequiredService<AppSettings>();
var fooService = new FooService();
fooService.Enable = settings.EnableFooService
return fooService;
});
services.AddTransient(serviceProvider =>
{
var settings = serviceProvider.GetRequiredService<AppSettings>();
var barService = new BarService();
barService.Enable = settings.EnableBarService
return barService;
});
The overload with IServiceProvider is available for i.e. AddSingleton, AddScoped, AddTransient. This pattern works great and is simple to implement, but often services have AddFoo() methods that do not provide this overload, i.e. AddApplicationInsightsTelemetry, AddCors, AddAuthentication, AddAuthorization...
I got inspiration from Ehsan Mirsaeedi answer:
https://stackoverflow.com/a/56278027/294242
Better pattern #2
We can implement the IConfigureOptions<TOptions> interface, register our configuration class in the
ConfigureServices method.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConfigureOptions<ApplicationInsightsServiceOptions>, ConfigureApplicationInsightsServiceOptions>();
services.AddApplicationInsightsTelemetry();
}
public class ConfigureApplicationInsightsServiceOptions : IConfigureOptions<ApplicationInsightsServiceOptions>
{
private readonly IServiceScopeFactory _serviceScopeFactory;
public ConfigureApplicationInsightsServiceOptions(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public void Configure(ApplicationInsightsServiceOptions options)
{
using var scope = _serviceScopeFactory.CreateScope();
var provider = scope.ServiceProvider;
var settings = provider.GetRequiredService<AppSettings>();
if (!settings.EnableTracking)
{
options.EnableQuickPulseMetricStream = false;
options.EnablePerformanceCounterCollectionModule = false;
options.EnableAppServicesHeartbeatTelemetryModule = false;
options.EnableAzureInstanceMetadataTelemetryModule = false;
options.EnableDependencyTrackingTelemetryModule = false;
options.EnableEventCounterCollectionModule = false;
options.EnableAdaptiveSampling = false;
options.EnableDebugLogger = false;
options.EnableHeartbeat = false;
options.EnableRequestTrackingTelemetryModule = false;
options.EnableAuthenticationTrackingJavaScript = false;
options.EnableDiagnosticsTelemetryModule = false;
}
}
}
I'm currently evaluating this pattern but i'm not sure Application Insights is completely disabled in my application.
I got inspiration from:
https://andrewlock.net/access-services-inside-options-and-startup-using-configureoptions/#the-new-improved-answer
In the basic example, there is no need to build a service provider. It is even advised against in most documentation. The desired settings can be extracted directly from configuration.
public void ConfigureService(IServiceCollection services) {
var section = configuration.GetSection(nameof(AppSettings));
// Configure the services for IOptions injection
services.Configure<AppSettings>(section);
// Extract the settings from configuration explicitly as needed
AppSettings appSettings = section.Get<AppSettings>();
// Conditionally include the service using the settings
if (appSettings.EnableApplicationInsights) {
services.AddApplicationInsightsTelemetry();
}
//...
}
There really is no need to involve dependent classes or use a custom IConfigureOptions<TOptions> to satisfy the desired conditional in Startup
Reference: Configuration in ASP.NET Core - Bind to an object graph

Add Custom Configuration Source to ASP.NET Core during Startup.Configure

While Microsoft provides an example of adding a custom configuration source in ConfigureAppConfiguration, that is too early for what I need to do, as I need DI to add services before I am ready or even know if I have custom providers to register. Is there anyway I can add to the configuration sources/providers during Startup.Configure? I'm fine this source is only available in subsequent requests after application startup.
In an ASP.NET Core 3.1 project, I've tried injecting IConfigurationRoot but I cannot find a way to add to the Providers enumerable. Any help you can offer would be great.
Here is some pseudo-pseudo code demonstrating what I would like to do in an ideal/fool's world:
public class Startup
{
private IConfigurationRoot ConfigurtionRoot;
public Startup(IWebHostEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonSettings(env.ContentRootPath, env.EnvironmentName)
.AddEnvironmentVariables();
ConfigurationRoot = builder.Build();
}
public void ConfigureServices(IServicesCollection services)
{
services.AddServicesNeededForCustomConfigProvider();
}
public void Configure(IApplicationBuilder app)
{
var provider = app.ApplicationServices.GetRequiredService<ICustomConfigProvider>();
// This is where we need some magic to add providers/sources after the initial configuration is built.
ConfigurationRoot.AddProvider(provider);
}
}

Autofac inject into ValidationAttribute on WebApi and OWIN

There's a few posts on SO relating to the work around for allowing autofac injections with ValidationAttribute 's (Asp.Net MVC3: Set custom IServiceProvider in ValidationContext so validators can resolve services) but I'm using OWIN and WebApi so I'm not sure if this is possible?
All other dependency injection is working fine.
DependencyResolver isn't populated in OWIN and I remember reading a difference in how OWIN handles injections for the validation requests. Does anyone know if Autofac, OWIN, WebApi and ValidationAttribute 's are possible? And, with specific examples?
You need to register the Autofac middleware and then you need to extend it to the WebApi. Now you can use the Autofac resolution inside the OWIN middleware.
// Register the Autofac middleware FIRST.
app.UseAutofacMiddleware(container);
// extend the autofac scope to the web api
app.UseAutofacWebApi(HttpConfiguration);
After this, WebApi and OWIN middleware will share the same resolution context, and you can do whatever you want.
For the ValidationAttribute thing you can, for example, do something like this:
public class AppStartup
{
public void Configuration(IAppBuilder app)
{
// Get your HttpConfiguration. In OWIN, you'll create one
// rather than using GlobalConfiguration.
var config = new HttpConfiguration();
//Set builder
var builder = new ContainerBuilder();
//IoC container build
var container = builder.Build();
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(HttpConfiguration);
WebApiConfig.Register(HttpConfiguration);
app.UseWebApi(HttpConfiguration);
}
}
and then
public override bool IsValid(object value)
{
var dependencyResolver = (AutofacWebApiDependencyResolver)GlobalConfiguration.Configuration.DependencyResolver;
using (var lifetimeScope= dependencyResolver.BeginScope())
{
var foo = lifetimeScope.Resolve<Foo>();
// use foo
}
}