Each method in Azure Functions can have a Microsoft.Extensions.Logging.ILogger injected into it for logging. Using WebJobsStartup with a startup class you can change the logging to use Serilog using the following syntax:
[assembly: WebJobsStartup(typeof(Startup))]
namespace MyFuncApp {
public class Startup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
builder.Services.AddLogging(
lb => lb.ClearProviders()
.AddSerilog(
new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.File(#"C:\Temp\MyFuncApp.log")
.CreateLogger(),
true));
}
}
}
I can also add other objects to the DI and inject them either into the methods or into the constructor for the class containing the methods using i.e. builder.Services.AddSingleton<IMyInterface, MyImplementation>();
However, I would very much like to be able to inject the Microsoft.Extensions.Logging.ILogger in the same way, but if I try to use the ILogger in the constructor I get the following error during method invokation (as that's when the class is created):
Microsoft.Extensions.DependencyInjection.Abstractions: Unable to resolve service for type 'Microsoft.Extensions.Logging.ILogger' while attempting to activate 'MyFuncApp.MyFunctions'.
So, is there any way of injecting the ILogger into a class constructor like this?
public class MyFunctions
{
private IMyInterface _myImpl;
private ILogger _log;
public MyFunctions(
IMyInterface myImplememtation, // This works
ILogger log) // This does not
{
_myImpl = myImplementation;
_log = log;
_log.LogInformation("Class constructed");
}
public async Task<IActionResult> Function1([HttpTrigger() ... ) {
_log.LogInformation("Function1 invoked");
}
}
Please try the code below, it works at my side:
[assembly: WebJobsStartup(typeof(Startup))]
namespace MyApp
{
public class Startup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
//other code
builder.Services.AddLogging();
}
}
public class Functions
{
//other code
private ILogger _log;
public Functions(ILoggerFactory loggerFactory)
{
_log = loggerFactory.CreateLogger<Functions>();
}
[FunctionName("Token")]
public async Task<IActionResult> Function1(
[HttpTrigger()]...)
{
_log.LogInformation("Function1 invoked");
}
}
}
It is possible to further simplify the necessary setup by using the package Anotar.Serilog.Fody (and any other Anotar package for that matter)
You need to set up Serilog all the same in the Startup class.
However, with the Fody package you can completely get rid of the injected logger
using Anotar.Serilog;
public class Functions
{
[FunctionName("Token")]
public async Task<IActionResult> Function1(
[HttpTrigger()]...)
{
// static calls to the LogTo class
// get translated into proper Serilog code during build
LogTo.Information("Function1 invoked");
}
}
With AzureFunctions v3, the pattern you outlined in your question works out-of-the box.
Related
Azure Functions V2 now supports .net dependency injection
In order to achieve that you need to do the following code:
[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]
namespace MyNamespace
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddHttpClient();
builder.Services.AddSingleton((s) => {
return new CosmosClient(Environment.GetEnvironmentVariable("COSMOSDB_CONNECTIONSTRING"));
});
builder.Services.AddSingleton<ILoggerProvider, MyLoggerProvider>();
}
}
}
I want to change the default container from .net to "Lamar" DI framework.
On their documentation they have an example for a WebHost:
var builder = new WebHostBuilder();
builder
// Replaces the built in DI container
// with Lamar
.UseLamar()
// Normal ASP.Net Core bootstrapping
.UseUrls("http://localhost:5002")
.UseKestrel()
.UseStartup<Startup>();
builder.Start();
But I'm not able to change IFunctionsHostBuilder to use "UseLamar()" extension. Since this extends IWebHostBuilder.
The only ways I was able to intercept the initialization of azure functions was Either with FunctionsStartup that configures IFunctionsHostBuilder or IWebJobsStartup that configures IWebJobsBuilder, but I don't find extensions for those kinds of builds on Lamar.
I've tried to check the existing extension to create a similar code but is not working because probably I need to create more stuff:
[assembly: FunctionsStartup(typeof(FunctionAppPrototype.Startup))]
namespace FunctionAppPrototype
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var container = new Container(x =>
{
x.AddTransient<IMyService, MyService>();
});
builder.Services.AddSingleton<IServiceProviderFactory<IServiceCollection>, LamarServiceProviderFactory>();
builder.Services.AddSingleton<IServiceProviderFactory<ServiceRegistry>, LamarServiceProviderFactory>();
}
}
}
After some research, I was able to find a solution using Autofac. I was not able to do it with Lamar it had no extension either for IFunctionsHostBuilder or IWebJobsBuilder.
Source Code: Binding extensions for dependency injection in Azure Function v2
Nuget: Willezone.Azure.WebJobs.Extensions.DependencyInjection
First, you need to intercept the startup of the function app by doing the following code:
[assembly: WebJobsStartup(typeof(AutoFacFunctionAppPrototype.WebJobsStartup))]
namespace AutoFacFunctionAppPrototype
{
public class WebJobsStartup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder) =>
builder.AddDependencyInjection<AutoFacServiceProviderBuilder>();
}
}
Then create the container and register the dependencies:
namespace AutoFacFunctionAppPrototype.Builders
{
public class AutoFacServiceProviderBuilder : IServiceProviderBuilder
{
private readonly IConfiguration configuration;
public AutoFacServiceProviderBuilder(IConfiguration configuration)
=> this.configuration = configuration;
public IServiceProvider Build()
{
var services = new ServiceCollection();
services.AddTransient<ITransientService, TransientService>();
services.AddScoped<IScopedService, ScopedService>();
var builder = new ContainerBuilder();
builder.RegisterType<SingletonService>().As<ISingletonService>().SingleInstance();
builder.Populate(services); // Populate is needed to have support for scopes.
return new AutofacServiceProvider(builder.Build());
}
}
}
Then you can use them on the function using the attribute [Inject]:
namespace AutoFacFunctionAppPrototype.Functions
{
public static class CounterFunction
{
[FunctionName("Counter")]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
[Inject]ITransientService transientService,
[Inject]IScopedService scopedService,
[Inject]ISingletonService singletonService,
ILogger logger)
{
logger.LogInformation("C# HTTP trigger function processed a request.");
string result = String.Join(Environment.NewLine, new[] {
$"Transient: {transientService.GetCounter()}",
$"Scoped: {scopedService.GetCounter()}",
$"Singleton: {singletonService.GetCounter()}",
});
return new OkObjectResult(result);
}
}
}
Using this approach I was only able to inject parameters, was not able to do constructor or property injection, even though I was a non-static class.
Note: If in the future Autofac supports extension for IFunctionsHostBuilder, probably would be better to use that approach instead of IWebJobsStartup.
I've created a new way to use Autofac DI in a Azure Functions v3 project, without using static functions or inject attributes. The disposal of services are called using the appropriate scope.
GitHub: https://github.com/junalmeida/autofac-azurefunctions
NuGet: https://www.nuget.org/packages/Autofac.Extensions.DependencyInjection.AzureFunctions
Feel free to contribute with me!
Example
public class Function1
{
private readonly IService1 _service1;
public Function1(IService1 service1)
{
_service1 = service1;
}
[FunctionName(nameof(Function1))]
public async Task Run([QueueTrigger("myqueue-items", Connection = "AzureWebJobsStorage")]string myQueueItem, ILogger log)
{
...
}
In .Core project I have an interface for logging:
public interface ILogger
{
void Trace(string format, params object[] args);
.........
void Fatal(string format, params object[] args);
}
Which is used in a log service interface:
public interface ILogService
{
ILogger GetLogger(string name);
ILogger GetLogger(Type typeName);
ILogger GetLogger<T>();
}
In .Droid project that interface is implemented:
public class AndroidLogService : ILogService
{
public ILogger GetLogger(string name)
{
return new AndroidLogger(name);
}
public ILogger GetLogger(Type typeName)
{
return GetLogger(typeName.Name);
}
public ILogger GetLogger<T>()
{
return GetLogger(typeof(T));
}
}
In .Droid Setup.cs file AndroidLogService is registered:
Mvx.LazyConstructAndRegisterSingleton<ILogService, AndroidLogService>();
Mvx.LazyConstructAndRegisterSingleton<IFileService, AndroidFileService>();
and finally used in some file in .Droid project:
private readonly ILogger _logger;
public AndroidFileService(IContextService contextService, ILogService logService, IEncryptionService encryptionService)
{
_logger = logService.GetLogger<AndroidFileService>();
.......
}
Finally logs work like this:
_logger.Warn("Failed to retrieve logo. Error: {0}", ex);
My doubts and questions:
AndroidFileService is never called with params that described above but from MvvmCross docs I've read that it's called Construction Injection
Ok, I understand this part but one thing is dark for me:
Where ILogger implementation exists?
I didn't find in the whole solution any part with something like
Mvx.RegisterType<ILogger , SomeLogger>();
How it can be? What a mechanism is used to register ILogger?
I found the answer.
I went inside AndroidLogService in Setup.cs:
public class AndroidLogService : ILogService
{
public ILogger GetLogger(string name)
{
return new AndroidLogger(name);
}
public ILogger GetLogger(Type typeName)
{
return GetLogger(typeName.Name);
}
public ILogger GetLogger<T>()
{
return GetLogger(typeof(T));
}
}
Then I went inside AndroidLogger:
using NLog;
using ILogger = ....Services.Support.ILogger;//from .Core project
namespace ....Android.Services.Support//from .Droid project
{
public class AndroidLogger : ILogger
{
private readonly NLog.ILogger _logger;
public AndroidLogger(string name)
{
_logger = LogManager.GetLogger(name);
}
.................
where I see that is used NLog which is built-in implementation of ILogger.
So, when the build was created for android I inserted this snippet in 2 files: one from .Core project and another from .Droid
_logger = logService.GetLogger<SomeViewModel>();
String name = logService.GetType().Name;
_logger.Debug("LogService name = {0} ", name);
which resulted for both cases as LogService name = AndroidLogService.
Before this check I thought that as .Core project doesn't have any reference to .Droid project, so, there are different implementations for them, but I was wrong:
Interface implemented in .Droid/.iOs project also works for .Core project.
Try this, if ILogger has an implementation in MvvmCross you would get the object populated.
var logger = Mvx.Resolve<ILogger>();
Serilog allows creating a context-aware logger:
Log.ForContext<T>()
I would like to register Serilog with SimpleInjector in such a way that T is the type of the consumer, i.e. it is which class it is injected in to.
e.g.
public class Car
{
public Car(ILogger logger) <= would be injected using Log.ForContext<Car>()
{
}
}
I can see this has been done with AutoFac.
And looking through the SimpleInjector documentation, there is a very promising overload of RegisterConditional() (with the Func<TypeFactoryContext, Type> parameter).
c.RegisterConditional(typeof (ILogger),
x => Log.ForContext(x.Consumer.ImplementationType), <= won't compile as expecting a Type
Lifestyle.Scoped,
x => true);
however, I don't want to tell SimpleInjector which Type to build, but how to build one.
I have integrated Serilog with Simple Injector with the following code based on #Steven genius answer on StackOverflow: logger wrapper best practice
public interface ILogger
{
void Log(LogEntry entry);
}
public class SerilogLogger<T> : ILogger
{
private readonly Serilog.ILogger _logger;
public SerilogLogger()
{
_logger = new LoggerConfiguration()
.WriteTo
.Trace(LogEventLevel.Information)
.CreateLogger()
.ForContext(typeof (T));
}
public void Log(LogEntry entry)
{
/* Logging abstraction handling */
}
}
public static class ContainerExtensions {
public static void RegisterLogging(this Container container)
{
container.RegisterConditional(
typeof(ILogger),
c => typeof(SerilogLogger<>).MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
c => true);
}
}
In your Composition Root:
var container = new Container();
container.RegisterLogging();
I have an ASP.NET Core 1.0 Solution with the following project structure:
Web App (ASP.NET MVC6)
BusinessLibrary (Class Library Package)
DataLibrary(Class Library Package)
Tests (Class Library Package w/ XUnit)
I am attempting to use Microsoft's new built-in dependency injection all throughout the entire system.
Here is how everything currently flows from my ASP.NET MVC App all the way down to my Repository layer
//Startup.cs of MVC Web App
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddSingleton(_=> Configuration);
services.AddTransient<ICustomerService, CustomerService>();
services.AddTransient<ICustomerRepository, CustomerRepository>();
}
public class CustomersController : Controller
{
private ICustomerService _service;
public CustomersController(ICustomerService service)
{
_service= service;
}
}
public class CustomerService : ICustomerService
{
private ICustomerRepository _repository;
public PriceProtectionManager(ICustomerRepository repository)
{
_repository = repository;
}
}
public class CustomerRepository : BaseRepository, ICustomerRepository
{
public CustomerRepository(IConfigurationRoot config)
: base(config)
{
}
}
public class BaseRepository
{
private IConfigurationRoot _config;
public BaseRepository(IConfigurationRoot config)
{
_config = config;
}
}
Now how can I get something similar to work with XUnit project so I can access CustomerService and call the functions?
Here is what my Fixture class looks like:
public class DatabaseFixture : IDisposable
{
public ICustomerService CustomerService;
public DatabaseFixture(ICustomerService service)
{
CustomerService = service;
}
public void Dispose()
{
}
}
The problem is that ICustomerService is unable to be resolved... This is probably because I don't have a Startup.cs like my WebApp. How do I replicate this behavior with the test project? I don't know where to create my TestServer because if I do it in the fixture it will be too late.
Well, you can provide your own dependencies to your SUT (which is the way you should want it IMHO). I've just answered a similar question here.
If you want to define your connectionstring at one place you could use xUnit's ability to use shared context (fixtures).
Update: Examples incorperating fixtures and DI...
Your testclass should implement IClassFixture and contain for example the following fields and constructor:
public class AspnetCoreAndXUnitPrimeShould: IClassFixture<CompositionRootFixture>
{
private readonly TestServer _server;
private readonly HttpClient _client;
private readonly CompositionRootFixture _fixture;
public AspnetCoreAndXUnitPrimeShould(CompositionRootFixture fixture)
{
// Arrange
_fixture = fixture;
_server = new TestServer(TestServer.CreateBuilder(null, app =>
{
app.UsePrimeCheckerMiddleware();
},
services =>
{
services.AddSingleton<IPrimeService, NegativePrimeService>();
services.AddSingleton<IPrimeCheckerOptions>(_ => new AlternativePrimeCheckerOptions(_fixture.Path));
}));
_client = _server.CreateClient();
}
Notice that AspnetCoreAndXUnitPrimeShould is the name of the testclass in my example. The fixture looks like:
public class CompositionRootFixture
{
public string Path { get; }
public CompositionRootFixture()
{
Path = "#/checkprime";
}
}
This is just a quick adoptation from another example, but you should understand how you can fix your problem now. AlternativePrimeCheckerOptions takes a string in the constructor, just like your Configuration class could. And with a fixture you arrange this connectionstring at one place.
Update
Sample: https://github.com/DannyvanderKraan/ASPNETCoreAndXUnit
Trying to do dependency injection into my SignalR Hub class using the SignalR-Server which is part of ASP.NET 5 (repo). I tried to figure this out from the tutorial at this link but I can't seem to identify how I can do this given that GlobalHost is no longer available. Here's what I'm trying to do:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
services.AddSingleton<IState, State>();
}
public void Configure(IApplicationBuilder app)
{
app.UseSignalR();
}
MyHub.cs
public class MyHub : Hub
{
public IState State { get; set; }
// SignalR accepts this parameterless ctor
public MyHub()
{
}
// SignalR won't use this because it has a parameter
public MyHub(IState state)
{
State = state;
}
}
How can I get SignalR-Server to use the MyHub(IState state) constructor injecting the required dependencies?
the best way (for Asp.Net 5) create a custom resolver to DefaultDependencyResolver that receives IServiceProvider:
public class CustomSignalRDependencyResolver : DefaultDependencyResolver
{
private readonly IServiceProvider _serviceProvider;
public CustomSignalRDependencyResolver(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public override object GetService(Type serviceType)
{
var service = _serviceProvider.GetService(serviceType);
return service ?? base.GetService(serviceType);
}
}
Then on StartUp class
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IState, State>();
//... other services
GlobalHost.DependencyResolver = new CustomSignalRDependencyResolver(services.BuildServiceProvider());
}
I managed to resolve this by adding my State class as a Singleton for IState in Startup.ConfigureServices, and then making a ServiceProvider property publicly available on my Startup.cs class. From there, I was able to GetRequiredService within the constructor of my SignalR Hub class. It isn't the ideal solution and hopefully I'll be able to adjust this to use constructor/property injection as the platform reaches RC.
Here's my code:
Startup.cs
public static IServiceProvider __serviceProvider;
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
services.AddSingleton<IState, State>();
__serviceProvider = services.BuildServiceProvider();
}
public void Configure(IApplicationBuilder app)
{
app.UseSignalR();
}
MyHub.cs
public class MyHub : Hub
{
public IState State { get; set; }
public MyHub()
{
State = (IState) Startup.__serviceProvider.GetRequiredService(typeof (IState));
}
public override Task OnConnected()
{
State.Clients = Clients;
State.Groups = Groups;
return base.OnConnected();
}
}
In this way, I was able to set properties and call methods on IState implementing objects from within MyHub, allowing me to persist my app state in memory.
You're very close. You just need:
public class MyHub : Hub
{
readonly IState _state;
public MyHub(IState state)
{
_state = state;
}
}
Ok. Now, I used Autofac, which I am not sure it has ASP.NET 5 integration yet. But if(for now) only target .NET 4.6, you should be fine.
I just published this repository which contains a basic project setup with SignalR and Autofac for dependency injection.
Now, I did the dependency injection setup in order to achieve the following:
be able to inject dependencies into my hub
be able to get the context for my hubs in order to send to clients from outside the hub without using GlobalHost (which is no longer available in .NET 5, but also shouldn't be used since it's a static global object)
I hope you manage to setup your project (even though I don't think you will be able to keep DNX in your build options since Autofac doesn't have the library .NET 5 ready yet.
I hope this helps! Best of luck!
https://github.com/radu-matei/SignalRDependencyInjection
EDIT: If you want to use NInject (and build your own dependency resolver if you want to target DNX, you can follow this repository from the official guys from SignalR (actually from the guy who wrote SignalR):
https://github.com/DamianEdwards/NDCLondon2013/tree/master/DependencyInjection
In this demo they use NInject to create their own dependency resolver, so you shouldn't have any problems targeting DNX if you have NInject libraries.
UPDATE: After reading a little about Dependency Injection in ASP.NET 5, it seems that it is done in an unified manner. If you haven't had a look at this article, I recommend it, even though it doesn't specifically show SignalR DI.
I have simply made constructor with dependencies. For example, I need my IUnitOfWork instance (which was configured in startup) in hub. That is working code
[HubName("receipts")]
public class ReceiptsHub : Hub
{
public IUnitOfWork<string> UnitOfWork { get; set; }
public ReceiptsHub(IUnitOfWork<string> unitOfWork) : base()
{
UnitOfWork = unitOfWork;
}
public override Task OnConnected()
{
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
return base.OnDisconnected(stopCalled);
}
}
In .NET 5 you can directly resolve the IServiceProvider and later you can have the required service. Please check the below codes:
public class MyHub : Hub
{
public IState State { get; set; }
private readonly IServiceProvider _serviceProvider;
public MyHub(IServiceProvider serviceProvider)
{
_serviceProvider=serviceProvider;
State = _serviceProvider.GetRequiredService<IState>();
}
public override Task OnConnected()
{
State.Clients = Clients;
State.Groups = Groups;
return base.OnConnected();
}
}