Conditional OperationProcessor in NSwag - c#

I try to add conditionally an OperationProcessor in NSwag. For example, the DefaultApiValueOperationProcessor should only be added/enabled when we are in a development environment (env.IsDevelopment)
Unfortunately I can't retrieve IHostingEnvironment in ConfigureServices, and also I can't get the Swagger's OperationProcessors on Configure, see code example at the comment lines:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSwaggerDocument(s =>
{
// can't get IHostingEnvironment here? (for env.IsDevelopment())
s.OperationProcessors.Add(new DefaultApiValueOperationProcessor("version", "1"));
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// can't get Swagger's OperationProcessors here?
app.UseOpenApi();
app.UseSwaggerUi3(o =>
{
o.DocExpansion = "list";
o.DocumentTitle = "My API";
});
app.UseMvc();
}
}
Any idea how I could fix this?

To access the web host environment from ConfigureServices, simply add a WebHostEnvironment property to the Startup class and set it from the constructor:
public class Startup
{
private IConfiguration Configuration { get; }
private IWebHostEnvironment WebHostEnvironment { get; }
public Startup(IConfiguration configuration, IWebHostEnvironment webHostEnvironment)
{
Configuration = configuration;
WebHostEnvironment = webHostEnvironment;
}
public void ConfigureServices(IServiceCollection services)
{
if (WebHostEnvironment.IsDevelopment())
{
// ...
}
}
}
I also put the Configuration property in this example because a lot of programs need it anyway.
Please note that the type is IWebHostEnvironment and not IWebHostingEnvironment because the latter has been deprecated.
Regarding your second question (how to access the operation processor from Configure), could you please shed a light on your intentions? I have no idea what you're trying to achieve.

Related

Write ConfigureServices Method In Another Class File

We have a template for our projects in our company in which they write AddTransient<>() methods inside another class. I want to know how to put ConfigureServices method of startup inside another class.
See we have a very simple startup project like this:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddTransient<LocationService, LocationService>();
services.AddTransient<PersonService, PersonService>();
services.AddTransient<UserService, UserService>();
services.AddAutoMapper(typeof(Startup));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
I want to move my (dependent class registration) to another class inside my project.
So this will be my new Startup.cs:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddAutoMapper(typeof(Startup));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
And this will be my ExampleFileName.cs :
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<LocationService, LocationService>();
services.AddTransient<PersonService, PersonService>();
services.AddTransient<UserService, UserService>();
}
First, you have to create a folder named Extensions and there you will create a file name whatever you want. I give my file name is ApplicationService.
Extensions/ApplicationServiceExtensions.cs
namespace API.Extensions
{
public static class ApplicationServiceExtensions
{
public static IServiceCollection AddApplicationServices(this IServiceCollection services, IConfiguration config)
{
services.AddTransient<LocationService, LocationService>();
services.AddTransient<PersonService, PersonService>();
services.AddTransient<UserService, UserService>();
return services;
}
}
}
startup.cs
private readonly IConfiguration _config;
public Startup(IConfiguration config)
{
_config = config;
}
public IConfiguration Configuration { get; }
//clarify code
public void ConfigureServices(IServiceCollection services)
{
services.AddApplicationServices(_config);
//clarify code
}
So,it's an example of putting the ConfigureServices method of startup inside another class. and which you want.
I Typed this blind, but is this what you want?
public abstract class StartUpBase
{
public virtual void ConfigureServices(IServiceCollection services)
{
services.AddTransient<LocationService, LocationService>();
services.AddTransient<PersonService, PersonService>();
services.AddTransient<UserService, UserService>();
}
}
public class StartUp : StartUpBase
{
public override ConfigureServices(IServiceCollection services)
{
services.AddControllers();
base.ConfigureServices(services);
services.AddAutoMapper(typeof(Startup));
}
}
Alternative:
public static class Another
{
public static IServiceCollection AddTransitentServices(IServiceCollection services)
{
//add your transistent services here
return services;
}
}
public class StartUp
{
public override ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services = Another.AddTransitentServices(services)
services.AddAutoMapper(typeof(Startup));
}
}

How to add new key/values to "IConfiguration" in ASP.NET Core

The "Configuration" object loads all appsettings.json content successfully (with "CreateDefaultBuilder" in Program.cs). The same "Configuration" object is accessible in "Startup.cs" as well (as it is injected by framework itself).
Now, in "Startup.ConfigureServices", I would like add add more entries to "Configuration" object and access it in "Startup.Configure" and in other classes (like controllers, etc.)
In simple words, I would like to have something like the following:
Configuration.add("MyNewKey", "MyNewValue"); //HOW TO DO THIS
At this moment, I don't want to use any structured types.
Is this possible at all?
Is this possible at all?
It is possible. We can set a configuration value in the Startup.ConfigureServices method and access it in the "Startup.Configure" and in other classes (like controllers, etc.) You could check the following sample code (Asp.net core 3.1 MVC application):
Startup.cs:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
// using get and set accessor
public IConfiguration Configuration { get; set; }
public void ConfigureServices(IServiceCollection services)
{
Configuration["MyNewKey"] = "AAA"; //set the configuration value.
services.AddControllersWithViews();
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
var result = Configuration["MyNewKey"]; //access the configuration value.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}
}
HomeController.cs:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IConfiguration _configuration;
public HomeController(ILogger<HomeController> logger, IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
}
public IActionResult Index()
{
var value = _configuration["MyNewKey"];
return View();
}
The debug screenshot as below:

Unable to run commands. Error: No database provider has been configured for this DbContext

I am unable to run EntityFramework Core commands against the basic .NET Core 2.2 Web API I created. The API is working, but I can not 'add-migration' or 'update-database' unless I change where the connection string is retrieved. I believe the first example is best practice because the connection string is more secure, but I get an error when trying to run EF Core commands.
"No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext."
As far as I can tell I have correctly used AddDbContext() while passing DbContextOptions<>. The only code differences are in Startup.ConfigureServices() and MyContext.OnConfiguring(). What am I doing wrong with my preferred example?
Preferred Example (EF Commands do not work)
// MyContext.cs
public class MyContext : DbContext
{
private readonly DbContextOptions<MyContext> _options;
private readonly IConfiguration _config;
public MyContext (DbContextOptions<MyContext> options, IConfiguration config) : base(options)
{
_options = options;
_config = config;
}
public DbSet<Account> Accounts { get; set; }
public DbSet<User> User { get; set; }
}
}
// Startup.cs
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyContext>(
options => options.UseSqlServer(Configuration.GetConnectionString("MyAPI")));
services.AddScoped<IMyRepository, MyRepository>();
services.AddAutoMapper();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); }
else { app.UseHsts(); }
//app.UseHttpsRedirection();
app.UseMvc();
}
}
With the code below I am able to run 'add-migration' and 'update-database' with no errors, but I believe the retrieval of the connection string is less secure this way.
Example 2 (EF Commands Work)
// MyContext.cs
public class MyContext : DbContext
{
private readonly DbContextOptions<MyContext> _options;
private readonly IConfiguration _config;
public MyContext (DbContextOptions<MyContext> options, IConfiguration config) : base(options)
{
_options = options;
_config = config;
}
public DbSet<Account> Accounts { get; set; }
public DbSet<User> User { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_config.GetConnectionString("MyAPI"));
}
}
}
// Startup.cs
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyContext>();
services.AddScoped<IMyRepository, MyRepository>();
services.AddAutoMapper();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); }
else { app.UseHsts(); }
//app.UseHttpsRedirection();
app.UseMvc();
}
}
You just need to specify where the DB connection is being created.
dotnet ef migrations add SomeMigration --startup-project ../path/to/Api/project
In this example, you are running the migration command and specifying the path to the project which contains the configuration/set up of the database context. If you don't specify this, and the setup is not in the DbContext class, then EF doesn't know how the database should be configured.
What fixed the issue for me was updating to .NET Core 3.1.101 and EFCore 3.1.1.

How to fix "Null exception" when seeding and initialising the database in asp.net core (Entity Framework)

I'm trying to seed my data for the first time run, using the configure function in Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
.
.
app.SeedData();
}
the app.SeedData(); then calls the SeedData function in DataSeeder.cs to initialise the ApplicatonDbContext.
public static class DataSeeder
{
public static async void SeedData(this IApplicationBuilder app)
{
var dbManager = app.ApplicationServices.GetService<ApplicationDbContext>();
dbManager.Database.Migrate();
Brand Addiction;
Addiction = new Brand()
{
BrandName = "Addiction"
};
dbManager.Brands.Add(Addiction);
dbManager.SaveChanges();
return;
}
}
When the code tries to run the dbManager.Database.Migrate();, it throws a NullReferenceException saying that dbManager is null. I tried putting a breakpoint on the first line of ApplicationDbContext.cs, seems like it doesn't run it. Any ways on how to fix it?
I've seen others using app.ApplicationServices.GetRequiredService<ApplicationDbContext>(); instead of app.ApplicationServices.GetService<ApplicationDbContext>();. I tried changing that and got an InvalidOperationException instead, saying "No service for type 'BrandTest.Data.ApplicationDbContext' has been registered."
My ApplicationDbContext.cs code btw
using BrandTest.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace BrandTest.Data
{
public class ApplicationDbContext : DbContext
{
public DbSet<Brand> Brands { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Server=DESKTOP-ABCDE\SQLEXPRESS;Database=PetStoreDB;Trusted_Connection=True;MultipleActiveResultSets=True");
base.OnConfiguring(optionsBuilder);
}
U have to add your context to DI in your Startup class (same class, where u have Configure() method)
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<ApplicationDbContext>();
...
}
this is Startup class from one of my projects:
public class Startup
{
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
Configuration = configuration;
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public static IConfiguration Configuration { get; private set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddOptions<ActivationServiceOptions>(Configuration);
services.AddScoped<ActivationServiceContext>();
services.AddOptions<ActivationServiceContextOptions>(Configuration);
services.AddOptions<KeysProviderOptions>(Configuration);
services.AddScoped<IEncryptor, Encryptor>();
LandingLoggingStartup.ConfigureServices(services, Configuration);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
else
app.UseHsts();
LandingLoggingStartup.Configure(app);
app.UseMvc();
}
}
InvalidOperationException instead, saying "No service for type 'BrandTest.Data.ApplicationDbContext' has been registered."
For this error message, you need to register your dbcontext in Startup.cs like
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(
Configuration.GetConnectionString("MySqlConnection"));
});
And you could not resolve the service from root provider, change DataSeeder like
public static class DataSeeder
{
public static async void SeedData(this IApplicationBuilder app)
{
var dbManager = app.ApplicationServices.CreateScope().ServiceProvider.GetRequiredService<ApplicationDbContext>();
dbManager.Database.Migrate();
Brand Addiction;
Addiction = new Brand()
{
BrandName = "Addiction"
};
dbManager.Brands.Add(Addiction);
dbManager.SaveChanges();
return;
}
}

How to store connection string to session in ASP.NET Core?

I am new to ASP.NET Core and I would like to access the connection string through the session but I am not sure at which point I should set the session value.
I can access connection string in Startup.cs but I think I cannot access HttpContext from there.
Here is the Startup.cs file
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(15);
});
// Here I can get connection string, but I cannot access Session
var connection = Configuration.GetConnectionString("devDb2");
services.AddDbContext<attWebApiContext>(options => options.UseSqlServer(connection));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSession();
app.UseMvc();
}
}

Categories

Resources