How can I use ILogger<T> from an Azure Functions V2 function? - c#

I have some services that were initially designed to be called from my ASP.NET Core 2.1 web application. They have dependencies injected to their constructors using Microsoft.Extensions.DependencyInjection package stuff. Some of them have a dependency of ILogger logger.
public GroupService(ILogger<GroupService> logger)
{
...
}
I am building a service provider within the function so that they can still work as expected however I'm wondering what I should do about the logger dependencies. An Azure Function (V2) gets an ILogger injected into it by default but that can't be used in the DI container to create the additional loggers that the services require.
Is there a LoggerFactory registered somewhere "under the covers" that I can get access to to be used in my DI container? I think that would allow me to add additional loggers that log to the functions output window or am I completely misunderstanding how logging in a function would work?
Do I just need to set up a new LoggerFactory and make that log to the ApplicationInsights instance used by the functions project?

Using the most recent Azure Function runtime, you can now have the runtime inject your dependencies through constructors.
You can now remove the static keywords from your Azure Function and have the runtime.
Here is how you can set this up:
[assembly: WebJobsStartup(typeof(StartUp))]
public class StartUp : IWebJobsStartup
{
public void Configure(IWebJobsBuilder webJobsBuilder)
{
// Enables logging and sets the minimum level of logs.
webJobsBuilder.Services.AddLogging(loggingBuilder =>
{
loggingBuilder.SetMinimumLevel(LogLevel.Debug);
});
// Registers your services.
webJobsBuilder.Services.AddTransient<IGroupService, GroupService>();
}
}

Related

Logging Mass Transit with Serilog

I have this console application project where I use EF6 with postgresql, Quartz and Mass Transit and as DI I use Castle Winsdor. The goal of the project is to check periodically a folder (or folders) for new files and process them (mostly by storing data in the DataBase).
I wanted to use a logging service for debug purposes and I came across Serilog. I managed to add it to Quartz and EF, but I have issues adding it to Mass Transit.
What I've done so far:
I've added Serilog as Logging for Castle.Core.Logging
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Auto(Classes.FromThisAssembly());
var factory = container.Resolve<ILoggerFactory>();
container.AddFacility<LoggingFacility>(f => f.LogUsing(factory as SerilogFactory));
}
And then I added it to EF using a Logger Interceptor and to Quartz using Serilog settings.
But now I want to add it to my MassTransit Automatonymous, but I'm having trouble figuring it out.
First of all, I tried to add Serilog from the BusControl Configuration:
BusControl = Bus.Factory.CreateUsingInMemory(cfg =>
{
cfg.UseSerilog(Log.Logger); //Where `Log.Logger` is the Serilog Logger
});
But then I encountered a MassTransit.Logging.ILogger exception that was also discussed here.
In that thread I found that I should add Serilog (or any other logger) in a different way, by configuring the MassTransit LogContext like so
LogContext.ConfigureCurrentLogContext(loggerFactory);. But the thing is, they are now using
Microsoft.Extensions.Logging.Abstractions and I cannot convert my logger from Castle.Core.Logging.ILogger to Microsoft.Extensions.Logging.ILogger.
Is there any way to use my Castle.Core.Logging logger?
You need to use the Serilog.Extensions.Logging NuGet package, which adds ILoggerFactory support to Serilog. You can pass that interface to configure MassTransit via:
LogContext.ConfigureCurrentLogContext(loggerFactory);
MassTransit does not use any Castle facilities, however, there is a NuGet package for configuring MassTransit using ]Castle Windsor](https://masstransit-project.com/usage/containers/castlewindsor.html).
Update: You can view a sample Serilog configuration in this sample

Upgrade to .NET Core 3.1 - Can't instantiate singleton PhysicalFileProvider in services

I have a .NET Core solution containing an API project that was targeting .NET Core 2.1, and I upgraded it to .NET Core 3.1. I've realized that there is a number of breaking changes from doing that, which I have gone through and modified to be compatible (such as converting UseMvc to UseRouting and so). But now I am stuck on one:
When I try to run the API project, I get this runtime error:
Some services are not able to be constructed (Error while validating
the service descriptor 'ServiceType:
Microsoft.Extensions.FileProviders.IFileProvider Lifetime: Singleton
ImplementationType:
Microsoft.Extensions.FileProviders.PhysicalFileProvider': No
constructor for type
'Microsoft.Extensions.FileProviders.PhysicalFileProvider' can be
instantiated using services from the service container and default
values.)
In Startup.cs, ConfigureServices, there is:
services.AddSingleton<IFileProvider, PhysicalFileProvider>();
which of course is the issue. But I'm not sure how do I convert this to be both compatible and have the API work the way it has been.
I did find an article on file providers that states:
The FileProviderSample sample app creates the provider in the
Startup.ConfigureServices method using
IHostEnvironment.ContentRootFileProvider:
var physicalProvider = _env.ContentRootFileProvider;
But it's not clear as to exactly where within ConfigureServices that is supposed to go... or if that replaces the AddSingleton... or how that's going to affect the API's behavior. So I'm not sure what to do with this.
How would DI create an instance of PhysicalFileProvider without specifying a root directory?
You could create an instance by yourself specifying the root, then registering that instance into the services collection.
This works for me:
public void ConfigureServices(IServiceCollection services) {
// (...)
IFileProvider fp = new PhysicalFileProvider("C:\\path\\to\\by\\root");
// or, you could get the file provider from the environment:
// IFileProvider fp = _env.ContentRootFileProvider;
services.AddSingleton<IFileProvider, PhysicalFileProvider>(_ => fp);
// (...)
}
Then, I just inject it into my DI managed class:
IFileProvider _fileProvider;
public FilesController(IFileProvider fp) {
this._fileProvider = fp;
}
As an alternative, and If you don't mind changing the constructor parameters of your class, you could inject IWebHostEnvironment instead. Then you could have easy access to the file provider from environment.

How to add all registered services from Blazor to Simple Injector?

I have a server side blazor application and I'm also using simple injector for other dependency injections in my application.
I want to register all services from blazor to the simple injector container because otherwise I will have to duplicate all service registration but also the main problem is that there's some services registrations that I don't know what is the correct way of registering (usually comes from some dependency injection extension library).
For example, I need something like
public void ConfigureServices(IServiceCollection services)
{
// multiple service registration
services.RegisterSomeServices();
// ...
// some how register all services into simple injector container
container.SomeHowRegisterServices();
}
How can I get all registered services from blazor and register in the simple injector container?
After some time searching, I found in the docs a ServiceCollection Integration Guide which is a way of integrating third party services into the Simple Injector.
What I need to do according to Cross wiring framework and third-party services is cross wire blazor services into simple injector, so for my case, what I need to do is
public void ConfigureServices(IServiceCollection services)
{
// multiple service registration
services.RegisterSomeServices();
// ...
// some how register all services into simple injector container
IServiceProvider provider = services
.AddSimpleInjector(simpleInjectorContainer)
.BuildServiceProvider(validateScopes: true);
// Ensures framework components are cross wired.
provider.UseSimpleInjector(simpleInjectorContainer);
}

Autofac Injection of data into OWIN Startup class

I've been looking around on SO for a while and can't find the answer to my particular question:
How do you use Autofac to inject data (via constructor or property) into the OWIN Startup class?
I'm using Autofac in a Windows service which also handles creation of back-end services etc. so I'm doing all the configuration reading and container building in there.
I want to be able to register the Startup class so I can inject allowed origins for CORS but even when I register the object with a property like so:
var builder = new ContainerBuilder();
// This will actually be read from config.
var origins = new[]
{
"http://localhost",
"http://localhost:8082",
};
builder.Register(c => new Startup(origins));
var container = builder.Build();
At runtime when the Startup class is instantiatedthe callstack shows it comes from external code and my Autofac builder didn't push the property in.
I'd ideally like to keep my Autofac registrations in a single place (the Windows service class) and just inject the required data to Startup so I can just do something like this below (where allowedOrigins is set as a property or injected via the constructor)
public void Configuration(IAppBuilder app)
{
var configuration = new HttpConfiguration();
...
var origins = string.Join(",", allowedOrigins);
var cors = new EnableCorsAttribute(origins, "*", "*") { SupportsCredentials = true };
configuration.EnableCors(cors);
....
}
Any ideas would be much appreciated.
Thanks
peteski
UPDATE
I should add that after attempting Autofac registration and building, I was kicking off the OWIN self host app by doing:
var webApp = WebApp.Start<Startup>(baseAddress);
From a conversation with a friend they suggested creating the Startup object and passing it to the WebApp:
var startup = new Startup(origins);
var webApp = WebApp.Start(startup, baseAddress);
Here, I'd add the IEnumerable<string> origins to the ctor for the Startup class. This does actually work! But feels like it goes around using Autofac to handle giving the Startup class what it needs.
Out of the box Autofac doesn't support what you're looking for.
In an IIS or other non-self-hosted environment, there's no hook for building a container and then injecting into the startup class. It's sort of a chicken/egg problem - the startup class is your hook to start building the container.
In a self-hosted environment, the suggestion you mentioned - creating the startup object and passing that into the call to WebApp.Start - is your best bet.
Basically, at some point you're going to hit the entry point for your application (usually in OWIN that's the startup class) and that's the point at which you have to build your container and be the bootstrapper for resolving and starting things up. Autofac can't help you with that - that's up to the framework(s) you're using (e.g., MVC, OWIN, Web API, etc.).

Hangfire configuration and Ninject configuration

I have an MVC 5 application which uses Ninject and I am adding Hangfire to it.
When I have added Ninject, I have used the NinjectWebCommon nuget package because of its simplicity in the configuration. So for now Ninject is configured through the NinjectWebCommon class which create a standard kernel and add the bindings.
Moreover I have created some custom module that I load when creating the kernel
private static IKernel CreateKernel() {
var kernel = new StandardKernel( new MyCustomModule() );
try {
kernel.Bind<Func<IKernel>>().ToMethod( ctx => () => new Bootstrapper().Kernel );
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices( kernel );
return kernel;
}
catch {
kernel.Dispose();
throw;
}
}
The Ninject Web Common is registered through the WebActivatorEx class
[assembly: WebActivatorEx.PreApplicationStartMethod( typeof( MyProject.Web.NinjectWebCommon ), "Start" )]
[assembly: WebActivatorEx.ApplicationShutdownMethodAttribute( typeof( MyProject.Web.NinjectWebCommon ), "Stop" )]
Now the problem is related on how to make Hangfire to see the Ninject configuration. By looking at the Hangfire.Ninject package I can read
The package provides an extension method for IGlobalConfiguration
interface:
var kernel = new StandardKernel();
GlobalConfiguration.Configuration.UseNinjectActivator(kernel);
Now my question are:
Because of the IGlobalConfiguration interface, I should add the Hangfire Ninject configuration inside the OWIN startup method (where the Hangfire config is already placed). How should I get the current Ninject Kernel (the one that NinjectWebCommon has configured?
What about the order of execution? Is the WebActivatorExexecuting before or after the OWIN startup?
What happens if I try to execute the configuration twice?
More generally, how can I share the Ninject configuration between the two?
How should I get the current Ninject Kernel
Looking at the code of Ninject.Web.Common.Bootstrapper shows that it stores a single static instance of the kernel, and exposes it via the Kernel property. This means that you can do this inside the OWIN startup method:
GlobalConfiguration.Configuration.UseNinjectActivator(
new Ninject.Web.Common.Bootstrapper().Kernel);
and you'll have the same IKernel instance, complete with whatever bindings you configured in NinjectWebCommon.RegisterServices
What about the order of execution? Is the WebActivatorEx executing before or after the OWIN startup?
Before. You can verify this (as I did) by setting breakpoints in each. More info
More generally, how can I share the Ninject configuration between the two?
What happens if I try to execute the configuration twice?
The kernel configuration is the "composition root." According to Mark Seemann, a preeminent expert on the subject, there should only be one of these in the application, and it should be as close as possible to the application's entry point.

Categories