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);
});
})
Related
I am using Quartz.Net on a Net 6 Worker project with Serilog for Logging.
I am using a custom JobFactory:
public class JobFactory : IJobFactory {
private readonly IServiceProvider _provider;
public JobFactory(IServiceProvider provider) {
_provider = provider;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) {
Logger logger = _provider.GetRequiredService<Logger>();
IDataService service = _provider.GetRequiredService<IDataService>();
Manager manager = new Manager(logger, service);
return new ManagerJob(manager, logger);
}
}
In NewJob method service is defined but logger is undefined. No idea why.
I am using Serilog and the configuration is Program.cs is:
Serilog.Log.Logger = new Serilog.LoggerConfiguration().WriteTo.Console().CreateBootstrapLogger();
try {
Serilog.Log.Information("Starting up");
IHostBuilder builder = Host.CreateDefaultBuilder(args);
builder.UseSerilog((context, configuration) = configuration.WriteTo.Console(LogEventLevel.Verbose)
});
builder.ConfigureServices((context, services) => {
services.AddQuartz(x => {
x.SchedulerId = "Bot";
x.UseMicrosoftDependencyInjectionJobFactory();
x.UseJobFactory<JobFactory>();
x.UseSimpleTypeLoader();
x.UseInMemoryStore();
x.UseDefaultThreadPool(y => {
y.MaxConcurrency = 20;
});
foreach (Manager manager in getManagers()) {
x.ScheduleJob<TraderJob>(
y => y.WithIdentity(manager.Name).WithCronSchedule(manager.Cron),
y => y.WithIdentity(manager.Name).UsingJobData(new JobDataMap((IDictionary<String, Object>) new Dictionary<String, Object> { { "Strategy", manager.Strategy } }))
);
}
});
services.AddQuartzHostedService(x => {
x.WaitForJobsToComplete = true;
});
services.AddTransient<IDataService, DataService>();
});
using IHost host = builder.Build();
await host.RunAsync();
}
catch (Exception exception) {
Serilog.Log.Fatal(exception, "Unhandled exception");
}
finally {
Serilog.Log.Information("Shutdown complete");
Serilog.Log.CloseAndFlush();
}
What am I missing?
if you want to get Logger
Logger logger = _provider.GetRequiredService<Logger>();
maybe you should services.Addsingleton<Logger>(Serilog.Log.Logger) in your Program.cs
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?
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>();
}
}
The following .Net core 2.0 console application got null value when running serviceProvider.GetService<Application>();?
class Program
{
static void Main(string[] args)
{
var services = new ServiceCollection();
ConfigureServices(services);
var serviceProvider = services.BuildServiceProvider();
var app = serviceProvider.GetService<Application>(); // Got null value
Task.Run(() => app.Run()).Wait();
}
private static void ConfigureServices(IServiceCollection services)
{
ILoggerFactory loggerFactory = new LoggerFactory()
.AddConsole()
.AddDebug();
services.AddSingleton(loggerFactory); // Add first my already configured instance
services.AddLogging(); // Allow ILogger<T>
IConfigurationRoot configuration = GetConfiguration();
services.AddSingleton<IConfigurationRoot>(configuration);
// Support typed Options
services.AddOptions();
services.Configure<MyOptions>(configuration.GetSection("MyOptions")); // Error!
services.AddTransient<Application>();
}
private static IConfigurationRoot GetConfiguration()
{
return new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddXmlFile("App.config", optional: true).Build();
}
public class MyOptions
{
public string Name { get; set; }
}
public class Application
{
ILogger _logger;
MyOptions _settings;
public Application(ILogger<Application> logger, IOptions<MyOptions> settings)
{
_logger = logger;
_settings = settings.Value;
}
public async Task Run()
{
try
{
_logger.LogInformation($"This is a console application for {_settings.Name}");
}
catch (Exception ex)
{
_logger.LogError(ex.ToString());
}
}
}
}
I think it looks good, you just need to provide a singleton for your options.
var options = new MyOptions();
configuration.GetSection("MyOptions").Bind(options);
services.AddSingleton<MyOptions>(options);
Then, shift your app to use that singleton.
public Application(ILogger<Application> logger, MyOptions settings)
EDIT: What about IOptions Pattern?
Seems like others have suggested that you can use that syntax if you import the 1.0.0-rc2-final version of Microsoft.Extensions.Options.ConfigurationExtensions, but that will cascade into other references as well. Might not be worth it just to late-bind the options class.
All Working (less IOptions)
If that's an ok approach, then there is some other code in that section that doesn't function. Cleaned up version below. Hope that helps.
class Program {
static void Main(string[] args) {
var services = new ServiceCollection();
ConfigureServices(services);
var serviceProvider = services.BuildServiceProvider();
var app = serviceProvider.GetService<Application>(); // Got null value
Task.Run(() => app.Run()).Wait();
}
private static void ConfigureServices(IServiceCollection services) {
ILoggerFactory loggerFactory = new LoggerFactory()
.AddConsole() // Error!
.AddDebug();
services.AddSingleton(loggerFactory); // Add first my already configured instance
services.AddLogging(); // Allow ILogger<T>
IConfigurationRoot configuration = GetConfiguration();
services.AddSingleton<IConfigurationRoot>(configuration);
// Support typed Options
var myOptions = new MyOptions();
configuration.GetSection("MyOptions").Bind(myOptions);
services.AddSingleton<MyOptions>(myOptions);
services.AddTransient<Application>();
}
private static IConfigurationRoot GetConfiguration() {
return new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddXmlFile("App.config", optional: true).Build();
}
public class MyOptions {
public string Name { get; set; }
}
public class Application {
ILogger _logger;
MyOptions _settings;
public Application(ILogger<Application> logger, MyOptions settings) {
_logger = logger;
_settings = settings;
}
public async Task Run() {
try {
_logger.LogInformation($"This is a console application for {_settings.Name}");
} catch (Exception ex) {
_logger.LogError(ex.ToString());
}
}
}
}