How to fix obsolete ILoggerFactory methods? - c#

I upgraded my project to .NET Core 2.2.x and got an obsolete warning regarding the following code - both lines:
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
The suggestion to fix is The recommended alternative is AddConsole(this ILoggingBuilder builder). I thought that is what I am using.
What am I missing here?

I had the same issue today.
Remove your logging configuration from Startup.cs and go to your Program.cs file and add something like:
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
})
.Build();
This used the 'builder' because the variable 'logging' is an IloggingBuilder (whereas your code is still using ILoggerFactory)
UPDATE: The other method I just tried is to stay inside Startup.cs but move the logging stuff from the 'Configure' method to 'ConfigureServices' like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(loggingBuilder =>
{
loggingBuilder.AddConfiguration(Configuration.GetSection("Logging"));
loggingBuilder.AddConsole();
loggingBuilder.AddDebug();
});
}
Perhaps keeps the Program.cs less polluted...

The documentation's recommendation to use AddConsole(this ILoggingBuilder builder) is correct, but for that to work you need to add a reference to the NuGet package Microsoft.Extensions.Logging.Console.

I got the same warning when I was updating logging code from .Net Core 2.1 to 3.0. The recommended way to do the upgrade is documented on MSDN.
In my case, I was trying to get an instance of LoggerFactory for console which is pretty straightforward in .Net Core 3.0:
using (var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole()))
{
// use loggerFactory
}

Don't worry about it - this is the dumbest thing ever!
Note
The following code sample uses a ConsoleLoggerProvider constructor
that has been obsoleted in version 2.2. Proper replacements for
obsolete logging APIs will be available in version 3.0. In the
meantime, it is safe to ignore and suppress the warnings.
In case you thought you forgot what Obsolete meant - you hadn't! Don't worry about it and just ignore it for now - or suppress the warning (sorry I don't have the code for that to hand).
(Wish they'd put a better explanation for why this was done - that's what I mean by dumb.)

According to the issue opened on GitHub for this, the replacement methods are already being called if you use CreateDefaultBuilder() method in your Program.cs.
https://github.com/aspnet/Docs/issues/9829
The only issue I have is that I only turned these on for non-Production environments.. and don't see a way to do so going forward.

If you don't have access to the LoggerFactory.Create(), use can still use the ILoggerFactory with the AddProvider() method giving it a ConsoleLoggerProvider() but it is a bit of a pain if you want to do something simple. The problem is, ConsoleLoggerProvider() requires a IOptionsMonitor<ConsoleLoggerOptions> as a parameter and the easiest thing to do if you
you don't have access to the options mechanism in your code base (my problem), or
the real options mechanisms in your existing code base don't match up with IOptionsMonitor<>, or
you have other reasons not to use the ASP.Net options facilities
is to create a dummy class:
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Console;
using Microsoft.Extensions.Options;
class DummyConsoleLoggerOptionsMonitor : IOptionsMonitor<ConsoleLoggerOptions>
{
private readonly ConsoleLoggerOptions option = new ConsoleLoggerOptions();
public DummyConsoleLoggerOptionsMonitor(LogLevel level)
{
option.LogToStandardErrorThreshold = level;
}
public ConsoleLoggerOptions Get(string name)
{
return this.option;
}
public IDisposable OnChange(Action<ConsoleLoggerOptions, string> listener)
{
return new DummyDisposable();
}
public ConsoleLoggerOptions CurrentValue => this.option;
private sealed class DummyDisposable : IDisposable
{
public void Dispose()
{
}
}
}
You can then use your ILoggerFactory like:
factory.AddProvider(
new ConsoleLoggerProvider(
new DummyConsoleLoggerOptionsMonitor(LogLevel.Debug)));

Related

ASP.NET Core - IStartupConfigureServicesFilter not working

My project has a core library with a IWebHostBuilder extension that registers several services for all my ASP.NET Core apps that utilize it:
public static IWebHostBuilder ChaseDefaults<TStartup>(this IWebHostBuilder builder) where TStartup : class
{
builder = builder
.ConfigureServices((ctx, services) => services.AddSingleton<IStartupFilter>(new MyStartupFilter(ctx, services)))
.UseStartup<TStartup>()
.UseSetting(WebHostDefaults.ApplicationKey, ApplicationEnvironment.Name);
return builder;
}
I'm using the IStartupFilter interface to register a startup filter in order to call a few middlewares before the host's Startup.Configure method is called, which works great:
public class MyStartupFilter : IStartupFilter
{
private readonly WebHostBuilderContext _context;
public MyStartupFilter(WebHostBuilderContext context, IServiceCollection services)
{
_context = context;
}
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return (app) =>
{
// Some middlewares
next(app);
};
}
}
However, I also want to achieve the same behavior for ConfigureServices. To achieve this, I called a custom ConfigureServices method in my constructor of my startup filter. However, it does not seem right to do it this way.
I found about the IStartupConfigureServicesFilter which is in order to register a few services before the host's Startup.ConfigureServices method is called. I know this interface is deprecated, however I do still want to use it to achieve my goal as I can't think of another solution.
When I tested things, it seems like ASP.NET Core is ignoring this filter, and when searching on ASP.NET Core source code on GitHub it seems like there are no references for it at all.
Any idea how to achieve what I want without the use of this deprecated filter?

Using DI in ConfigureService-Methods (especially IApplicationLifetime) in .NET Core

I'm trying to implement a new extension method for the IWebHostBuilder that executes some code, like registering somewhere.
The important part is, that it also unregisters when the application is shutting down.
I added some code which shows what I want to do. The problem here is, that the instance of IApplicationLifetime is not available yet. This new extension method was added last in the pipeline of building the WebHost.
public static IWebHostBuilder UseHttpGatewayappRegistration(this IWebHostBuilder webHostBuilder)
{
webHostBuilder.ConfigureServices(services =>
{
var sp = services.BuildServiceProvider();
// This instance is not available
IApplicationLifetime applicationLifetime = sp.GetService<IApplicationLifetime>();
// These instances are ok
AppSettings appSettings = sp.GetService<AppSettings>();
ILoggerFactory loggerFactory = sp.GetService<ILoggerFactory>();
var registration = new Registration(loggerFactory.CreateLogger<Registration>());
applicationLifetime.ApplicationStopping.Register(() =>
{
registration.Unregister();
});
});
return webHostBuilder;
}
Why is the IApplicationLifetime instance null, even though I added this extension method last in the pipeline of building the WebHost pipeline? It would be great, if someone would provide me with some information about the execution order of all "ConfigureServices" methods and how or if it is at all possible to use IApplicationLifetime in a ConfigureServices method.
I know I could do this all without the WebHostBuilder, but it seems logical to me to do it there and I also think there has to be a way.
Unfortunately I couldn't find much information online...
Thank you.
EDIT: I found a way to use DI in ConfigureServices methods. I edited the question accordingly. This helped me: How to Resolve Instance Inside ConfigureServices in ASP.NET Core
You can't get access to IApplicationLifetime instance from ConfigureServices method. It's by design. Check the "Services Available in Startup" section here:
Application Startup in ASP.NET Core.
Resolve IApplicationLifetime in the IWebHostBuilder.Configure method (it replaces the Startup.Configure method if it is used later in IWebHostBuilder configuration pipeline):
public static IWebHostBuilder Foo(this IWebHostBuilder webHostBuilder)
{
webHostBuilder.Configure(x =>
{
var service = x.ApplicationServices.GetService<IApplicationLifetime>();
});
return webHostBuilder;
}
It's also possible to extend the original Startup.Configure method instead of replace (a lot of Reflection logic should be added to make this solution reliable):
.Configure(app =>
{
var env = app.ApplicationServices.GetService<IHostingEnvironment>();
dynamic startup = Activator.CreateInstance(typeof(TStartup), env);
//Carefully resolve all input parameters.
//Make sure all required services are registered in DI.
startup.Configure(app, env);
var lifetime = app.ApplicationServices.GetService<IApplicationLifetime>();
})

in ASP.NET Core, is there any way to set up middleware from Program.cs?

I am building a support library for ASP.NET Core websites. I have a few pieces of middleware that need to be enabled, and they need to be added before any other middleware due what they do.
I can create an extension method on IWebHostBuilder to add services, likewise for configuring logging, but I don't see any way to add middleware in a programmatic way. Is there any way to do this? Looking at the source for WebHost/WebHostBuilder nothing jumped out.
Given the first comment, I may not have been clear enough. I know how to create middleware and use it. What I am trying to do is ensure that when the Configure(IApplicationBuilder app) method is called on Startup by the framework, my middleware is already in place. In a similar manner to being able to do ServiceConfiguration prior to Startup even being created. So an extension method like
public static IWebHostBuilder AddPayscaleHostingServices(this IWebHostBuilder webHostBuilder, string serviceName)
{
return webHostBuilder.ConfigureServices(collection =>
{
collection.RegisterPayscaleHostingServices();
}).ConfigureLogging(factory =>
{
});
}
gives me the ability to do some setup prior to the webHostBuilder.Build method, but I don't see anything similar for middleware/anything on IApplicationBuilder.
Thanks,
Erick
You could use a startup filter to achieve this. Startup filters allow you to configure middleware from a service resolved from the DI container.
Defining a startup filter is easy:
public class MyStartupFilter : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
// Configure middleware
// ...
// Call the next configure method
next(app);
};
}
}
Always make sure to call next(app) or any other middleware won't be configured.
Now register the startup filter as a singleton implementation of IStartupFilter in your ConfigureServices method:
services.AddSingleton<IStartupFilter, MyStartupFilter>();

ASP.NET core mapping subdomains to areas

I am trying to map the subdomains to areas, So far all the answers I found were for pervious versions of .NET and not .NET core. the best and most relevant answer I found was from This page. however i am having a problem implementing it as it appears to be the walkthrough for a pervious version of .NET core and i am getting the 'MvcRouteHandler' does not contain a constructor that takes 0 arguments Error.
Here is the code which i get the error from:
public class AreaRouter : MvcRouteHandler, IRouter //the error happens in this line, visual studio underlines the AreaRoute word
{
public new async Task RouteAsync(RouteContext context)
{
string url = context.HttpContext.Request.Headers["HOST"];
string firstDomain = url.Split('.')[0];
string subDomain = char.ToUpper(firstDomain[0]) + firstDomain.Substring(1);
string area = subDomain;
context.RouteData.Values.Add("area", subDomain);
await base.RouteAsync(context);
}
}
so anyway, i am looking for another way to map subdomains to areas or find a way to fix this error.
Edit: Complete tutorial available here.
Managed to create working version of your file.
1. Code for Router is available here. Specify your own subdomains in _allowedSubdomains array on top of file.
2. After adding router, change your code in Startup.cs:
app.UseMvc(routes =>
{
routes.DefaultHandler = areaRouter;
areaRouter should be passed as described by #mepsi.
3. Finally, allow the use of sudomains as described here.
Everything should be working now. The complete code is available on Github. I will write complete tutorial with all explanations later.
I'm struggling with the same issue and I managed to get forward by adding my router on ConfigureServices with:
services.AddSingleton<AreaRouter>();
Then inject it into the Configure method with:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, AreaRouter areaRouter)
And finally set it in place:
routes.DefaultHandler = areaRouter;
Fixing this problem got me forward, but unfortunetaly I still couldn't get the subdomain routing to work as intended. It seems like routing decision is already made at this point.
Note: I would have only added comment but I can't do that yet.
If you look at the definition of MvcRouteHandler you will see it does not have a parameterless constructor, the ones availables are:
public MvcRouteHandler(IActionInvokerFactory actionInvokerFactory, IActionSelector actionSelector, DiagnosticSource diagnosticSource, ILoggerFactory loggerFactory);
public MvcRouteHandler(IActionInvokerFactory actionInvokerFactory, IActionSelector actionSelector, DiagnosticSource diagnosticSource, ILoggerFactory loggerFactory, IActionContextAccessor actionContextAccessor);
As you're inheriting from it, you must call the base constructor, so usually you would create a constructor with the same parameters and pass it to the base as you don't require any extra parameter:
public AreaRouter (IActionInvokerFactory actionInvokerFactory,
IActionSelector actionSelector, DiagnosticSource diagnosticSource,
ILoggerFactory loggerFactory, IActionContextAccessor actionContextAccessor)
: base(actionInvokerFactory, actionSelector, diagnosticSource,
loggerFactory, actionContextAccessor) { }

Using Startup class in ASP.NET5 Console Application

Is it possible for an ASP.NET 5-beta4 console application (built from the ASP.NET Console project template in VS2015) to use the Startup class to handle registering services and setting up configuration details?
I've tried to create a typical Startup class, but it never seems to be called when running the console application via dnx . run or inside Visual Studio 2015.
Startup.cs is pretty much:
public class Startup
{
public Startup(IHostingEnvironment env)
{
Configuration configuration = new Configuration();
configuration.AddJsonFile("config.json");
configuration.AddJsonFile("config.{env.EnvironmentName.ToLower()}.json", optional: true);
configuration.AddEnvironmentVariables();
this.Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.Configure<Settings>(Configuration.GetSubKey("Settings"));
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationContext>(options => options.UseSqlServer(this.Configuration["Data:DefaultConnection:ConnectionString"]));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(minLevel: LogLevel.Warning);
}
}
I've tried to manually create the Startup class in my Main method, but this doesn't seem like the right solution and hasn't so far allowed me to configure the services.
I'm assuming there's some way for me to create a HostingContext that doesn't start up a web server but will keep the console application alive. Something along the lines of:
HostingContext context = new HostingContext()
{
ApplicationName = "AppName"
};
using (new HostingEngine().Start(context))
{
// console code
}
However so far the only way I can get this to work is if I set the HostingContext.ServerFactoryLocation to Microsoft.AspNet.Server.WebListener, which starts up the web server.
What you're looking for is the right idea, but I think you'll need to back up a moment.
Firstly, you may have noticed that your default Program class isn't using static methods anymore; this is because the constructor actually gets some dependency injection love all on its own!
public class Program
{
public Program(IApplicationEnvironment env)
{
}
public void Main(string[] args)
{
}
}
Unfortunately, there aren't as many of the services you're used to from an ASP.NET 5 hosting environment registered; thanks to this article and the IServiceManifest you can see that there's only a few services available:
Microsoft.Framework.Runtime.IAssemblyLoaderContainer
Microsoft.Framework.Runtime.IAssemblyLoadContextAccessor
Microsoft.Framework.Runtime.IApplicationEnvironment
Microsoft.Framework.Runtime.IFileMonitor
Microsoft.Framework.Runtime.IFileWatcher
Microsoft.Framework.Runtime.ILibraryManager
Microsoft.Framework.Runtime.ICompilerOptionsProvider
Microsoft.Framework.Runtime.IApplicationShutdown
This means you'll get the joy of creating your own service provider, too, since we can't get the one provided by the framework.
private readonly IServiceProvider serviceProvider;
public Program(IApplicationEnvironment env, IServiceManifest serviceManifest)
{
var services = new ServiceCollection();
ConfigureServices(services);
serviceProvider = services.BuildServiceProvider();
}
private void ConfigureServices(IServiceCollection services)
{
}
This takes away a lot of the magic that you see in the standard ASP.NET 5 projects, and now you have the service provider you wanted available to you in your Main.
There's a few more "gotchas" in here, so I might as well list them out:
If you ask for an IHostingEnvironment, it'll be null. That's because a hosting environment comes from, well, ASP.Net 5 hosting.
Since you don't have one of those, you'll be left without your IHostingEnvironment.EnvironmentName - you'll need to collect it from the environment variables yourself. Which, since you're already loading it into your Configuration object, shouldn't be a problem. (It's name is "ASPNET_ENV", which you can add in the Debug tab of your project settings; this is not set for you by default for console applications. You'll probably want to rename that, anyway, since you're not really talking about an ASPNET environment anymore.)

Categories