This is the code in my Startup.cs file and 2 of my three methods are running on build. However I added the bottom method public void PackageRequestDataAccess and for some reason its not running.
namespace Company.Shipping.Service
{
public class Startup
{
private IHostingEnvironment _environment;
private IConfigurationRoot _configurationRoot;
public Startup(IHostingEnvironment env)
{
_environment = env;
}
public void ConfigureServices(IServiceCollection services)
{
//Code Ran successfully here
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifetime)
{
//Code running successfully here
}
//Method below not running
public void PackageRequestDataAccess(Common.ServiceHost.WebHost.ServiceConfiguration configuration, IServiceCollection services)
{
IMongoCollection<PackageDataEntity> _reqrespcollection;
MongoDBRepository<PackageDataEntity> _repo = new MongoDBRepository<PackageDataEntity>(configuration.ConnectionStrings["MongoDB"]);
_reqrespcollection = _repo.Collection;
int _expiry = Convert.ToInt32(configuration.Settings["ShippingReqRespDataTTL"]);
TimeSpan _ttl = new TimeSpan(0, 0, _expiry);
CreateIndexOptions index = new CreateIndexOptions();
index.ExpireAfter = _ttl;
var _key = Builders<PackageDataEntity>.IndexKeys.Ascending("RequestSentOn");
_reqrespcollection.Indexes.CreateOneAsync(_key);
}
}
}
I need to run all these three methods whenever the application starts.
As per the MSDN docs available here only the Configure and ConfigureServices are called during startup.
The Startup class must include a Configure method and can optionally
include a ConfigureServices method, both of which are called when the
application starts.
In you case, may be you can add your logic to any one of this method or just call the method from the above method(s).
Related
I have asp.net core 3.1 web api project. I have added the nuget package: Microsoft.FeatureManagement.AspNetCore
Add the below in the appsettings.local.json:
{
"FeatureManagement": {
"EnableNewFeature": true
}
}
Startup.cs
public class Startup
{
private readonly IConfiguration configuration;
private readonly IWebHostEnvironment webHostEnvironment;
private readonly IFeatureManager featureManager;
public Startup(IConfiguration configuration, IWebHostEnvironment webHostEnvironment, IFeatureManager featureManager)
{
this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
this.webHostEnvironment = webHostEnvironment ?? throw new ArgumentNullException(nameof(webHostEnvironment));
this.featureManager = featureManager ?? throw new ArgumentNullException(nameof(featureManager));
}
public virtual void ConfigureServices(IServiceCollection services) {
/// Code.Start
services.AddFeatureManagement();
/// Code.End
}
public async Task Configure(IApplicationBuilder app, L10NCacheInitializationService l10nIniService)
{
app.UseIf(await featureManager.IsEnabledAsync(AppKeys.EnableNewFeature), x => x.UseNewFeature());
}
}
On validation I came across the below error :
Unable to resolve service for type 'Microsoft.FeatureManagement.IFeatureManager' while attempting to activate 'Startup'.
Can anyone help me to resolve this issue?
You cannot inject IFeatureManager in to the constructor of Startup because it's not yet registered. Once registered you can get it using app.ApplicationServices.GetRequiredService
With using Microsoft.Extensions.DependencyInjection at the top of your file it would look something like this:
public class Startup
{
private readonly IConfiguration configuration;
private readonly IWebHostEnvironment webHostEnvironment;
public Startup(IConfiguration configuration, IWebHostEnvironment webHostEnvironment)
{
this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
this.webHostEnvironment = webHostEnvironment ?? throw new ArgumentNullException(nameof(webHostEnvironment));
}
public virtual void ConfigureServices(IServiceCollection services)
{
services.AddFeatureManagement();
}
public async Task Configure(IApplicationBuilder app, L10NCacheInitializationService l10nIniService)
{
var featureManager = app.ApplicationServices.GetRequiredService<IFeatureManager>();
app.UseIf(await featureManager.IsEnabledAsync(AppKeys.EnableNewFeature), x => x.UseNewFeature());
}
}
If you need to use IFeatureManager in the Startup class for other cases like using inside of the ConfigureServices method, you can use this extension method:
public static class ConfigurationExtensions
{
public static bool IsFeatureEnabled(this IConfiguration configuration, string feature)
{
var featureServices = new ServiceCollection();
featureServices.AddFeatureManagement(configuration);
using var provider = featureServices.BuildServiceProvider();
var manager = provider.GetRequiredService<IFeatureManager>();
return manager.IsEnabledAsync(feature).GetAwaiter().GetResult();
}
}
I have ASP.NET Core application where I need to get database credentials from external AWS service. Essentially, I need to inject CredentialRetrievalService into Startup.cs. Awhile ago, I found an example that describes perfectly how to do it:
In Program.cs:
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureServices(serviceCollection =>
serviceCollection.AddScoped<ISomeService, SomeService>())
.UseStartup<Startup>()
.Build();
}
and in Startup.cs:
private ISomeService _someService;
public Startup(IConfiguration configuration, ISomeService someService)
{
_someService = someService;
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
// Just to see that everything works - and it does!
services.AddMvc(options =>
{
options.MaxModelValidationErrors = _someService.GetNumber(Configuration.GetValue<int>("MaxModelValidationErrors"));
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvc();
}
Now I am trying to upgrade the application to ASP.NET Core 3. I can see that there is breaking change, and the code above doesn't work any more. Recommended action is to Inject services into the Startup.Configure method instead. But what should I do if I need to invoke injected service in ConfigureServices?
After more analysis, this is what seems to work:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddScoped<ISomeService, SomeService>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IOptions<MvcOptions> options, ISomeService someService)
{
// ...
int value = Configuration.GetValue<int>("MaxModelValidationErrors");
options.Value.MaxModelValidationErrors = someService.GetNumber(value);
// ...
}
But it doesn't work with my real application where SomeService is DbContext
Consider changing the approach and taking advantage of dependency injection when configuring options
Reference Use DI services to configure options
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services) {
//...
services.AddScoped<ISomeService, SomeService>();
services.AddMvc(); //options will be configured lower down
//Use DI services to configure MVC options
services.AddOptions<MvcOptions>()
.Configure<ISomeService>((options, someService) => {
int value = Configuration.GetValue<int>("MaxModelValidationErrors");
options.MaxModelValidationErrors = someService.GetNumber(value);
});
//...
}
//...
My web application needs to let an admin user add and remove served folders from a .net core 2 app. I have found a way to provide a list of served folders, but I can't find a way to dynamically add or remove them once the app has been configured.
How do I re-run the configure function from within the application? Alternatively, how do I add or remove UseFileServer() configurations within an already-running service?
public class Startup
{
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseDeveloperExceptionPage();
app.UseMvc();
//get these dynamically from the database
var locations = new Dictionary<string, string>{
{#"C:\folder1", "/folder1"},
{#"D:\folder2", "/folder2"}
};
foreach (var kvp in locations)
{
app.UseFileServer(new FileServerOptions()
{
FileProvider = new PhysicalFileProvider(
kvp.Key
),
RequestPath = new PathString(kvp.Value),
EnableDirectoryBrowsing = true
});
}
}
}
I'm using .net core 2.0.0-preview2-final.
You may want to dynamically inject the FileServer middleware based on your settings.
There is an example project on Microsoft's Chris Ross' Github: https://github.com/Tratcher/MiddlewareInjector/tree/master/MiddlewareInjector
You'll have to add the MiddlewareInjectorOptions, MiddlewareInjectorMiddleware and MiddlewareInjectorExtensions classes from the aforementioned repo to your project.
Then, in your Startup class, register the MiddlewareInjectorOptions as a singleton (so it's available throughout your application) and use the MiddlewareInjector:
public class Startup
{
public void ConfigureServices(IServiceCollection serviceCollection)
{
serviceCollection.AddSingleton<MiddlewareInjectorOptions>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseDeveloperExceptionPage();
var injectorOptions = app.ApplicationServices.GetService<MiddlewareInjectorOptions>();
app.UseMiddlewareInjector(injectorOptions);
app.UseWelcomePage();
}
}
Then, inject the MiddlewareInjectorOptions wherever you want and configure the middleware dynamically, like this:
public class FileServerConfigurator
{
private readonly MiddlewareInjectorOptions middlewareInjectorOptions;
public FileServerConfigurator(MiddlewareInjectorOptions middlewareInjectorOptions)
{
this.middlewareInjectorOptions = middlewareInjectorOptions;
}
public void SetPath(string requestPath, string physicalPath)
{
var fileProvider = new PhysicalFileProvider(physicalPath);
middlewareInjectorOptions.InjectMiddleware(app =>
{
app.UseFileServer(new FileServerOptions
{
RequestPath = requestPath,
FileProvider = fileProvider,
EnableDirectoryBrowsing = true
});
});
}
}
Note that this MiddlewareInjector can inject just a single middleware, so your code should call UseFileServer() for each path you want to serve.
I've created a Gist with the required code: https://gist.github.com/michaldudak/4eb6b0b26405543cff4c4f01a51ea869
I want to use a separate project to store the Controllers for my test app. Due to how ASP.NET Core is working, you need to call services.AddMvc().AddApplicationPart with the assembly you want to add.
I use Visual Studio 2017 (so no more project.json there)
The problem is I can't reach the assembly I want to include!
I added reference there in my project:
Also, I decided to use polyfill for AppDomian like so (to reach assembly I need):
public class AppDomain
{
public static AppDomain CurrentDomain { get; private set; }
static AppDomain()
{
CurrentDomain = new AppDomain();
}
public Assembly[] GetAssemblies()
{
var assemblies = new List<Assembly>();
var dependencies = DependencyContext.Default.RuntimeLibraries;
foreach (var library in dependencies)
{
if (IsCandidateCompilationLibrary(library))
{
var assembly = Assembly.Load(new AssemblyName(library.Name));
assemblies.Add(assembly);
}
}
return assemblies.ToArray();
}
private static bool IsCandidateCompilationLibrary(RuntimeLibrary compilationLibrary)
{
return compilationLibrary.Name == ("TrainDiary")
|| compilationLibrary.Dependencies.Any(d => d.Name.StartsWith("TrainDiary"));
}
}
But when I used it there wasonly one assembly in the list (only linked to this project itself, not one with Controllers).
Here is my Startup.cs:
public class Startup
{
public IConfigurationRoot Configuration { get; }
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
var assemblies = GetAssemblies(); // I tought I will find there Assmbly I need but no
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddReact();
services.AddMvc();
services.AddLogging();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseReact(config =>
{
});
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
public List<Assembly> GetAssemblies()
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
List<Assembly> currentAssemblies = new List<Assembly>();
foreach (Assembly assembly in assemblies)
{
if (assembly.GetName().Name.Contains("TrainDiary"))
{
currentAssemblies.Add(assembly);
}
}
return currentAssemblies;
}
}
How can I get my project to also lookup controllers in the other project? Why couldn't it see it?
UPD
Tried to make directly like in this exapmle here.
So my code started looks like that:
var controllersAssembly = Assembly.Load(new AssemblyName("TrainDiary.Controllers"));
services.AddMvc()
.AddApplicationPart(controllersAssembly)
.AddControllersAsServices();
And it succeed to get the assembly:
But when I try to call Controller function - it fails!
Controller code:
[Route("Login")]
public class LoginController: Controller
{
[Route("Login/Test")]
public void Test(string name, string password)
{
string username = name;
string pass = password;
}
}
Requests:
UPD2:
For some reason remove Routes helped:
public class LoginController: Controller
{
public void Test(string name, string password)
{
string username = name;
string pass = password;
}
}
So, if sum this up:
1) You need to Add Reference to your separate project with
controllers.
2) Configure services (part):
public void ConfigureServices(IServiceCollection services)
{
var controllersAssembly = Assembly.Load(newAssemblyName("SomeProject.MeetTheControllers"));
services.AddMvc().AddApplicationPart(controllersAssembly).AddControllersAsServices();
}
3) My configure looks like that (pay attention to UseMvcWithDefaultRoute):
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseReact(config =>
{
});
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
So in my case of controller like this you don't need to put Routes attribute here, just call it by Some/Test url
namespace SomeProject.MeetTheControllers.Test
{
using Microsoft.AspNetCore.Mvc;
public class SomeController: Controller
{
public void Test(string name, string notname)
{
string username = name;
string nan = notname;
}
}
}
I'm currently trying to write an AuthenticationMiddleware. See this answer. The app builds with no error but when I execute dnx web I get the following error:
Unable to locate suitable constructor for type 'Namespace.BasicAuthenticationMiddleware'. Ensure the type is concrete and all parameters are accepted by a constructor.
at Microsoft.Extensions.Internal.ActivatorUtilities.CreateInstance(IServiceProvider
provider, Type instanceType, Object[] parameters)
at Microsoft.AspNet.Builder.UseMiddlewareExtensions.<>c__DisplayClass2_0.b__0(RequestDelegate next)
at Microsoft.AspNet.Builder.Internal.ApplicationBuilder.Build()
at Microsoft.AspNet.Hosting.Internal.HostingEngine.BuildApplication()
fail: Microsoft.AspNet.Hosting.Internal.HostingEngine[7]
I'm sure that the Constructor signature I use is wrong in some way but I'm not able to find a suitable documentation for this, since it seems like there are dozens of deprecated ones.
This is the AuthenticationMiddleware:
public class BasicAuthenticationMiddleware : AuthenticationMiddleware<BasicAuthOptions>
{
public BasicAuthenticationMiddleware(
RequestDelegate next,
BasicAuthOptions options,
ILoggerFactory loggerFactory,
IUrlEncoder urlEncoder)
: base(next, options, loggerFactory, urlEncoder) {}
protected override AuthenticationHandler<BasicAuthOptions> CreateHandler()
{
return new BasicAuthenticationHandler();
}
}
BasicAuthOptions:
public class BasicAuthOptions : AuthenticationOptions {
public const string Scheme = "BasicAuth";
public BasicAuthOptions()
{
AuthenticationScheme = Scheme;
AutomaticAuthenticate = true;
}
}
BasicAuthenticationExtensions
public static class BasicAuthenticationExtensions
{
public static void UseBasicAuthentication(this IApplicationBuilder builder) {
builder.UseMiddleware<BasicAuthenticationMiddleware>(new ConfigureOptions<BasicAuthOptions>(o => new BasicAuthOptions()));
}
}
Startup.cs:
public class Startup
{
public Startup(IHostingEnvironment env)
{
// Set up configuration sources.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddAuthorization(options => {
options.AddPolicy(BasicAuthOptions.Scheme, policy => policy.Requirements.Add(new BasicAuthRequirement()));
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseIISPlatformHandler();
app.UseStaticFiles();
app.UseBasicAuthentication();
app.UseMvc();
}
// Entry point for the application.
public static void Main(string[] args) => Microsoft.AspNet.Hosting.WebApplication.Run<Startup>(args);
}
Your UseBasicAuthentication extension tries to inject a ConfigureOptions instance that your middleware doesn't take as a parameter.
Simply flow the options instance as-is:
public static class BasicAuthenticationExtensions {
public static void UseBasicAuthentication(this IApplicationBuilder builder) {
builder.UseMiddleware<BasicAuthenticationMiddleware>(new BasicAuthOptions());
}
}