I'm building .NetCore console app which uses SimpleRabbit (RabbitMQ).
I am attempting to use HostBuilder() to this purpose (dependecy injection, configuration, services).
I create a SimpleInjector container in the Bootstrap class.
I would like to inject this container for use into HostBuilder.
As you can see below, the FooSubscriberService constructor is expecting a container.
I see HostBuilder exposes a ConfigureContainer() method which sounds like what I'm looking for.
I've looked around but I'm unclear as how to use this/inject the container.
Supporting Class Snippets
public class FooSubscriberService : IFooSubscriberService
{
Container container;
private readonly ILogger<FooSubscriberService> logger;
public FooSubscriberService(Container container, ILogger<DiscoverySubscriberService> logger)
{
this.container = container;
this.logger = logger;
}
....
}
public class QueueManagementService : BasicRabbitService, IQueueManagementService
{
ILogger _logger;
public QueueManagementService(RabbitConfiguration config, ILogger<QueueManagementService> _logger) : base(config)
{
this._logger = _logger;
}
....
}
Container Creation
public class Bootstrap
{
public static Container container;
public static void ConfigureServices(IConfigurationRoot configurationRoot)
{
container = new SimpleInjector.Container();
container.Options.ResolveUnregisteredConcreteTypes = false;
container.Options.EnableAutoVerification = false;
RabbitConfiguration rabbitConfiguration = new RabbitConfiguration();
configurationRoot.GetSection("RabbitConfiguration").Bind(rabbitConfiguration);
var runtimeModel = new RuntimeConfigurationModel();
var runtimeStatus = new RuntimeServicesStatus();
container.ConfigureServices(rabbitConfiguration, runtimeModel, runtimeStatus, searchJobStatus);
}
}
Code Snippet for initializing and running HostBuilder.
Bootstrap.ConfigureServices(configurationRoot);
var container = Bootstrap.container;
var builder = new HostBuilder()
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
})
.ConfigureServices((context, services) =>
{
var config = context.Configuration;
services
.AddRabbitConfiguration(configurationRoot.GetSection("RabbitConfiguration"))
.AddSubscriberConfiguration(configurationRoot.GetSection("Subscribers"))
.AddPublisherServices()
.AddSubscriberServices()
.AddSingletonMessageHandler<FooSubscriberService>()
.AddSingleton<IQueueManagementService, QueueManagementService>()
.AddSingleton<IHostedService, FooConsoleService>();
});
// ??? .ConfigureContainer<Container>( => { }); ???
await builder.RunConsoleAsync();
What is the method for injecting my Container into HostBuilder()?
Are there additional NuGet packages I need to use?
Related
I'm trying to get instance of ILogger<T> when resolving a dependency in the Startup class of an Azure Function v4 in process version.
Only this doesn't work and will result in an exception, null or a not working logger.
I followed the following AutoFac documentation for Azure Functions.
I register some types and try to resolve a type in the Startup class. The problem is the ILogger<T>. In this case the Logger<EventBusServiceBus>.
In the sample I try to resolve IEventBus in the ConfigureEventBus method. Because of this, services.AddSingleton<IEventBus, EventBusServiceBus>(sp => ....); is trying to be resolved in the AddEventBus method.
I don't understand why it can't be resolved because I see some registered types in the Autofac container.
See the comments in the code.
Thanks!
Simplified Startup from the docs:
internal class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
// Use IServiceCollection.Add extension method to add features as needed, e.g.
//builder.Services.AddDataProtection();
builder.Services.AddSingleton(GetContainer(builder.Services));
// Important: Use AddScoped so our Autofac lifetime scope gets disposed
// when the function finishes executing
builder.Services.AddScoped<LifetimeScopeWrapper>();
builder.Services.Replace(ServiceDescriptor.Singleton(typeof(IJobActivator), typeof(AutofacJobActivator)));
builder.Services.Replace(ServiceDescriptor.Singleton(typeof(IJobActivatorEx), typeof(AutofacJobActivator)));
builder.Services.AddCustomIntegrations(configuration);
builder.Services.AddEventBus(configuration);
var serviceProvider = builder.Services.BuildServiceProvider();
ConfigureEventBus(serviceProvider);
}
private static IContainer GetContainer(IServiceCollection serviceCollection)
{
var containerBuilder = new ContainerBuilder();
containerBuilder.Populate(serviceCollection);
containerBuilder.RegisterModule<LoggerModule>();
// This is a convenient way to register all your function classes at once
containerBuilder.RegisterAssemblyTypes(typeof(Startup).Assembly)
.InNamespaceOf<Function1>();
// TODO: Register other dependencies with the ContainerBuilder like normal
return containerBuilder.Build();
}
private void ConfigureEventBus(ServiceProvider sp)
{
var eventBus = sp.GetRequiredService<BuildingBlocks.EventBus.Abstractions.IEventBus>();
// Unable to use eventBus here
}
}
Some extension methods:
static class CustomExtensionsMethods
{
public static IServiceCollection AddCustomIntegrations(this IServiceCollection services, IConfiguration configuration)
{
services.AddSingleton<IServiceBusPersisterConnection>(sp =>
{
var serviceBusConnectionString = configuration["EventBusConnection"];
var subscriptionClientName = configuration["SubscriptionClientName"];
return new DefaultServiceBusPersisterConnection(serviceBusConnectionString);
});
return services;
}
public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration)
{
services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
{
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>();
// Get autofac container so we rolve dependencies from there
var autoFacContainer = sp.GetRequiredService<IContainer>();
// This doesn't work
////var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>();
// This does work
var iLifetimeScope = autoFacContainer.Resolve<ILifetimeScope>();
// This doesn't work
//var logger = sp.GetRequiredService<ILogger<EventBusServiceBus>>();
// This doesn't work either but at least now no error/exception. Resolves into empty logger.
var loggerFactory = new LoggerFactory();
var logger = loggerFactory.CreateLogger<EventBusServiceBus>();
// Tried these for logging without luck.
// Don't understand because I see a loggerfactory and Ilogger in the container...
//var loggerFactory = autoFacContainer.ResolveNamed<ILoggerFactory>("loggerFactory");
//var loggerFactory = autoFacContainer.Resolve<ILoggerFactory>();
////var loggerFactory = sp.GetRequiredService<ILoggerFactory>();
////var logger = loggerFactory.CreateLogger<EventBusServiceBus>();
////autoFacContainer.Resolve<ILogger<EventBusServiceBus>>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
string subscriptionName = configuration["SubscriptionClientName"];
return new EventBusServiceBus(serviceBusPersisterConnection, logger,
eventBusSubcriptionsManager, iLifetimeScope, subscriptionName);
});
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
return services;
}
}
So I did found a working solution for now. Maybe someone has a better solution but this works for me for now...
public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration)
{
services.AddSingleton<IEventBus, EventBusServiceBus>(sp =>
{
var serviceBusPersisterConnection = sp.GetRequiredService<IServiceBusPersisterConnection>();
// Get autofac container so we resolve dependencies from there
var autoFacContainer = sp.GetRequiredService<IContainer>();
// This does work
var iLifetimeScope = autoFacContainer.Resolve<ILifetimeScope>();
// Create a new logger that is connected to the console with a minimum loglevel from host.json based on the namespace
var loggerFactory = LoggerFactory.Create(builder =>
{
var logLevel = configuration.GetValue<LogLevel>("AzureFunctionsJobHost:logging:logLevel:" + typeof(Startup).Namespace, LogLevel.Information);
builder.AddConsole();
builder.SetMinimumLevel(logLevel);
});
var logger = loggerFactory.CreateLogger<EventBusServiceBus>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
string subscriptionName = configuration["SubscriptionClientName"];
return new EventBusServiceBus(serviceBusPersisterConnection, logger,
eventBusSubcriptionsManager, iLifetimeScope, subscriptionName);
});
return services;
}
So the important part is this:
// Create a new logger that is connected to the console with a minimum loglevel from host.json based on the namespace
var loggerFactory = LoggerFactory.Create(builder =>
{
var logLevel = configuration.GetValue<LogLevel>("AzureFunctionsJobHost:logging:logLevel:" + typeof(Startup).Namespace, LogLevel.Information);
builder.AddConsole();
builder.SetMinimumLevel(logLevel);
});
var logger = loggerFactory.CreateLogger<EventBusServiceBus>();
I'm implementing a DI container in a .NET Core WPF app.
The below setup works fine, but I don't know how to inject an interface in any of my view model's constructor.
In the MainWindow I want to instantiate two or more pages with their respective dependencies (side menu item click). How do I get the dependency container in order to resolve those dependencies?
public partial class App : Application
{
private readonly ServiceProvider _serviceProvider;
private IConfiguration _configuration { get; set; }
public App()
{
InitializeConfiguration();
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
_serviceProvider = serviceCollection.BuildServiceProvider();
}
private void InitializeConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
_configuration = builder.Build();
}
private void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IConfiguration>(s => _configuration);
services.AddTransient<IRepository, Repository>();
services.AddTransient<IDeliveryService, DeliveryService>();
services.AddSingleton<MainWindow>();
}
private void OnStartup(object sender, StartupEventArgs e)
{
var mainWindow = _serviceProvider.GetService<MainWindow>();
mainWindow.Show();
}
}
Many thanks!
I'm trying to get Autofac dependency injection for ef core in a WPF Core 3.1 app but I cannot do any migrations.
I keep getting this error when trying to add migrations through CLI:
Unable to create an object of type 'ApplicationDbContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
Any suggestion?
Those are my methods:
Startup Class:
public partial class App : Application
{
public IServiceProvider ServiceProvider { get; private set; }
private void App_OnStartup(object sender, StartupEventArgs e)
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient(typeof(MainWindow));
ServiceProvider = serviceCollection.BuildServiceProvider();
var bootstrapper = new Bootstrapper(ServiceProvider);
var container = bootstrapper.Bootstrap();
var mainWindow = container.Resolve<MainWindow>();
//var mainWindow = new MainWindow(
// new RecruitmentViewModel(new ContractorDataService()));
mainWindow.Show();
}
}
Bootstrapper which acts like a start-up
private readonly IServiceProvider _serviceProvider;
public Bootstrapper(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IContainer Bootstrap()
{
var builder = new ContainerBuilder();
var config = new ConfigurationBuilder();
config.AddJsonFile("appsettings.json");
var configuration = config.Build();
builder.RegisterType<MainWindow>().AsSelf();
builder.RegisterType<RecruitmentViewModel>().AsSelf();
builder.RegisterType<ContractorDataService>().As<IContractorDataService>();
RegisterContext<ApplicationDbContext>(builder, configuration);
return builder.Build();
}
public void RegisterContext<TContext>(ContainerBuilder builder, IConfiguration configuration)
where TContext : DbContext
{
builder.Register(componentContext =>
{
var dbContextOptions = new DbContextOptions<TContext>(new Dictionary<Type, IDbContextOptionsExtension>());
var optionsBuilder = new DbContextOptionsBuilder<TContext>(dbContextOptions)
.UseApplicationServiceProvider(_serviceProvider)
.UseSqlServer(configuration.GetConnectionString("DefaultConnection"),
serverOptions => serverOptions.EnableRetryOnFailure(5, TimeSpan.FromSeconds(30), null));
return optionsBuilder.Options;
}).As<DbContextOptions<TContext>>()
.InstancePerLifetimeScope();
builder.Register(context => context.Resolve<DbContextOptions<TContext>>())
.As<DbContextOptions>()
.InstancePerLifetimeScope();
builder.RegisterType<TContext>()
.AsSelf()
.InstancePerLifetimeScope();
}
I am trying to use IHttpClientFactory in my solution instead of just instances of HttpClient.
startup.cs:
services.AddHttpClient("Test", client =>
{
client.BaseAddress = new Uri("http://localhost:57863");
client.Timeout = new TimeSpan(0, 0, 30);
client.DefaultRequestHeaders.Clear();
});
and in my services in need of a HttpClient:
private readonly Uri _clusterLinuxUri;
private readonly IHttpClientFactory _clientFactory;
public LiasseService(ConfigSettings settings, IHttpClientFactory clientFactory)
{
_clusterLinuxUri = new Uri($"{settings.LinuxClusterEndpoint}");
_clientFactory = clientFactory;
}
public async Task<LiasseDetails> CreateLiasseAsync(LiasseCreate liasseData)
{
using (var response = await _clientFactory.CreateClient("Test")
.PostAsJsonAsync($"{_clusterLinuxUri}{_createPath}", liasseData))
{
await response.CheckHttpError($"{nameof(CreateLiasseAsync)} - error in CL");
var detailsList = await response.Content.ReadAsAsync<LiasseDetailsList>();
return detailsList.Details.FirstOrDefault();
}
}
The part I haven't figured out is how to inject it in Autofac.
program.cs
private static void Main()
{
try
{
var builder = new ContainerBuilder();
builder.RegisterModule(new GlobalAutofacModule());
builder.RegisterServiceFabricSupport();
builder.RegisterStatelessService<FacadeCore>("xxx.FacadeCoreType");
using (builder.Build())
{
ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(FacadeCore).Name);
Thread.Sleep(Timeout.Infinite);
}
}
catch (Exception e)
{
ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString());
throw;
}
}
public class GlobalAutofacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<ConfigSettings>();
builder.RegisterType<PaymentService>().As<IPaymentService>();
builder.RegisterType<MailerService>().As<IMailerService>();
builder.RegisterType<LiasseService>().As<ILiasseService>();
builder.RegisterType<AnalyseFinanciereService>().As<IAnalyseFinanciereService>();
builder.RegisterType<ApimService>().As<IApimService>();
builder.RegisterType<UserRepository>().As<IUserRepository>();
builder.RegisterType<ApplicationProcessRepository>().As<IApplicationProcessRepository>();
builder.RegisterType<LiasseRepository>().As<ILiasseRepository>();
builder.RegisterType<CustomUserIdProvider>().As<IUserIdProvider>();
}
}
Am I supposed to create some custom client that implements IHttpClientFactory to be able to inject it? How should I do this? Any examples? Thanks.
Please see Interface documentation here
So to answer your question:
1) Using IServiceCollection from 'ConfigureServices' method call .AddHttpClient()
2) Create new Autofac container builder and populate it with IServiceCollection mentioned above
3) From ConfigureServices method return new AutofacServiceProvider
public IServiceProvider ConfigureServices(IServiceCollection services)
{
...
services.AddHttpClient();
var containerBuilder = new ContainerBuilder();
containerBuilder.Populate(services);
var container = containerBuilder.Build();
return new AutofacServiceProvider(container);
}
P.S.
Make sure to add - Autofac.Extensions.DependencyInjection nuget package, in order to be able to use AutofacServiceProvider class.
Apart from doing using a WebHostBuilder based solution similar to what you get when creating a vanilla Asp.Net Core Service Fabric project, you can alternatively just do this
public class HttpClientModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register(_ =>
{
var services = new ServiceCollection();
services.AddHttpClient();
var containerBuilder = new ContainerBuilder();
containerBuilder.Populate(services);
var container = containerBuilder.Build();
return new AutofacServiceProvider(container);
})
.As<IServiceProvider>()
.SingleInstance();
builder.Register(ctx =>
{
var scope = ctx.Resolve<IComponentContext>();
var provider = scope.Resolve<IServiceProvider>();
var factory = provider.GetService<IHttpClientFactory>();
return factory.CreateClient();
}).As<HttpClient>();
}
}
It's .NET Core 2.0 console application, using DI how can I pass parameters to constructor.
RabbitMQPersistentConnection class needs to pass parameter on constructer
RabbitMQPersistentConnection(ILogger logger, IConnectionFactory connectionFactory, IEmailService emailService);
My instance
var _emailService = sp.GetRequiredService();
not working like this when I initialize it as service
Program.cs
public static class Program
{
public static async Task Main(string[] args)
{
// get App settings
var builder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
IConfigurationRoot configuration = builder.Build();
//Initilize Service Collection
#region Initilize Service Collection
var serviceProvider = new ServiceCollection()
.AddLogging()
.AddEntityFrameworkSqlServer().AddDbContext<EmailDBContext>(option => option.UseSqlServer(configuration.GetConnectionString("connection_string")))
.AddSingleton<IEmailConfiguration>(configuration.GetSection("EmailConfiguration").Get<EmailConfiguration>())
.AddScoped<ISMTPService, SMTPService>()
.AddScoped<IEmailService, EmailService>()
.BuildServiceProvider();
#endregion
.ConfigureServices((hostContext, services) =>
{
services.AddLogging();
services.AddHostedService<LifetimeEventsHostedService>();
services.AddHostedService<TimedHostedService>();
services.AddEntityFrameworkSqlServer();
services.AddScoped<IRabbitMQPersistentConnection, RabbitMQPersistentConnection>(sp =>
{
var logger = sp.GetRequiredService<ILogger<RabbitMQPersistentConnection>>();
var _emailService = sp.GetRequiredService<IEmailService>(); // Not Working. :(
var _rabbitMQConfiguration = configuration.GetSection("RabbitMQConfiguration").Get<RabbitMQConfiguration>();
var factory = new ConnectionFactory()
{
HostName = _rabbitMQConfiguration.EventBusConnection
};
if (!string.IsNullOrEmpty(_rabbitMQConfiguration.EventBusUserName))
{
factory.UserName = _rabbitMQConfiguration.EventBusUserName;
}
if (!string.IsNullOrEmpty(_rabbitMQConfiguration.EventBusPassword))
{
factory.Password = _rabbitMQConfiguration.EventBusPassword;
}
return new RabbitMQPersistentConnection(logger, factory, _emailService);
});
})
.Build();
await host.RunAsync();
}
}
RabbitMQPersistentConnection.cs
public class RabbitMQPersistentConnection : IRabbitMQPersistentConnection
{
private readonly IConnectionFactory _connectionFactory;
EventBusRabbitMQ _eventBusRabbitMQ;
IConnection _connection;
IEmailService _emailService;
private readonly ILogger _logger;
bool _disposed;
public RabbitMQPersistentConnection(ILogger<RabbitMQPersistentConnection> logger, IConnectionFactory connectionFactory, IEmailService emailService)
{
_connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory));
_emailService = emailService;
_logger = logger;
}
}
You are creating your own IServiceProvider in your "Initilize Service Collection" region, which is a different instance of IServiceProvider to the one that you're using here:
var _emailService = sp.GetRequiredService<IEmailService>();
The registrations you've made in your region are simply being thrown away. In order to resolve this, you can just pull those registrations into your HostBuilder.ConfigureServices callback function:
.ConfigureServices((hostContext, services) =>
{
services.AddLogging();
services.AddHostedService<LifetimeEventsHostedService>();
services.AddHostedService<TimedHostedService>();
services.AddEntityFrameworkSqlServer();
services.AddDbContext<EmailDBContext>(option => option.UseSqlServer(configuration.GetConnectionString("connection_string")));
services.AddSingleton<IEmailConfiguration>(configuration.GetSection("EmailConfiguration").Get<EmailConfiguration>());
services.AddScoped<ISMTPService, SMTPService>();
services.AddScoped<IEmailService, EmailService>();
services.AddScoped<IRabbitMQPersistentConnection, RabbitMQPersistentConnection>(sp =>
{
var logger = sp.GetRequiredService<ILogger<RabbitMQPersistentConnection>>();
var _emailService = sp.GetRequiredService<IEmailService>();
var _rabbitMQConfiguration = configuration.GetSection("RabbitMQConfiguration").Get<RabbitMQConfiguration>();
var factory = new ConnectionFactory()
{
HostName = _rabbitMQConfiguration.EventBusConnection
};
if (!string.IsNullOrEmpty(_rabbitMQConfiguration.EventBusUserName))
{
factory.UserName = _rabbitMQConfiguration.EventBusUserName;
}
if (!string.IsNullOrEmpty(_rabbitMQConfiguration.EventBusPassword))
{
factory.Password = _rabbitMQConfiguration.EventBusPassword;
}
return new RabbitMQPersistentConnection(logger, factory, _emailService);
});
})