I am trying to migrate an IHost extension for EF Seed migration from .Net5 to .Net6 . Can you please lead me the proper way to do this conversion.
public static class HostExtensions
{
public static IHost MigrateDatabase<TContext>(this IHost host,
Action<TContext, IServiceProvider> seeder,
int? retry = 0) where TContext : DbContext
{
int retryForAvailability = retry.Value;
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
<code removed for brevity>
}
return host;
}
}
The extension is being called in .Net5 as follows:
public static void Main(string[] args)
{
CreateHostBuilder(args)
.Build()
.MigrateDatabase<OrderContext>((context, services) =>
{
var logger = services.GetService<ILogger<OrderContextSeed>>();
OrderContextSeed
.SeedAsync(context, logger)
.Wait();
})
.Run();
}
MigrateDatabase :
public static void MigrateDatabase(this IServiceProvider sp)
{
var loggerFactory = sp.GetRequiredService<ILoggerFactory>();
// operations
}
Program.cs :
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Services.MigrateDatabase();
apo.Run();
use like this in Program.cs :)
var app = builder.Build();
app.MigrateDatabase<WebSocketAcceptContext>();
Related
I've a .NET Core application that needs to peform operation based on a scheduler.
I've used the following code which also installs Kestrel but I don't need to use it at all
public class Program
{
public static void Main(string[] args)
{
var processModule = System.AppDomain.CurrentDomain.BaseDirectory;
var assemblyName = Assembly.GetCallingAssembly().GetName();
var version = assemblyName.Version;
Directory.SetCurrentDirectory(processModule);
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
var applicationName = configuration.GetValue<string>("Properties:Application");
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration).Enrich.WithProperty("Version", version).Enrich
.WithProperty("ApplicationName", applicationName)
.CreateLogger();
Log.Logger = logger;
Log.Logger.Information("Started {ApplicationName} with version : {Version}", applicationName, version);
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }).UseSerilog()
.UseWindowsService();
}
And the Startup.cs is as follow :
class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
DataConnection.DefaultSettings = new Linq2DBSettings(configuration);
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//OMISS
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
}
}
Is there a way I can have Startup.cs (or IServiceCollection ) so that I can initialize my DI in this way?
Thanks
If you have all your services available in separate libraries, or you at least have the option to move them there from Web app, you could create some extension to configure DI both in your Web and Console applications
Library project
using Microsoft.Extensions.DependencyInjection;
namespace ClassLibrary
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection ApplyMyServices(this IServiceCollection services)
{
services.AddScoped<MyService>();
return services;
}
}
public class MyService
{ }
}
Console app
using ClassLibrary;
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var serviceCollection = new ServiceCollection();
serviceCollection.ApplyMyServices();
var serviceProvider = serviceCollection.BuildServiceProvider();
using var scope = serviceProvider.CreateScope();
var myService = scope.ServiceProvider.GetService<MyService>();
}
}
}
Web app
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.ApplyMyServices();
}
I'm creating a project that is based on the eShopOnContainers Microservices architecture
I Made a few changes to program.cs and startup.cs according to .NET Core 3+
Program.cs:
public static IHostBuilder CreateHostBuilder(IConfiguration configuration, string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
Startup.cs:
// ConfigureContainer is where you can register things directly
// with Autofac. This runs after ConfigureServices so the things
// here will override registrations made in ConfigureServices.
// Don't build the container; that gets done for you by the factory.
public void ConfigureContainer(ContainerBuilder builder)
{
//configure autofac
// Register your own things directly with Autofac, like:
builder.RegisterModule(new MediatorModule());
builder.RegisterModule(new ApplicationModule(Configuration));
}
Now in Startup.cs the AddCustomIntegrations() method Registers the IRabbitMQPersistentConnection which returns the DefaultRabbitMQPersistentConnection with IConnectionFactory configured
public static IServiceCollection AddCustomIntegrations(this IServiceCollection services, IConfiguration configuration)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<IIdentityService, IdentityService>();
services.AddTransient<IVehicleManagementIntegrationEventService, VehicleManagementIntegrationEventService>();
services.AddTransient<Func<DbConnection, IIntegrationEventLogService>>(
sp => (DbConnection c) => new IntegrationEventLogService(c));
services.AddSingleton<IRabbitMQPersistentConnection>(sp =>
{
var logger = sp.GetRequiredService<ILogger<DefaultRabbitMQPersistentConnection>>();
var factory = new ConnectionFactory()
{
HostName = configuration["EventBusConnection"],
DispatchConsumersAsync = true
};
if (!string.IsNullOrEmpty(configuration["EventBusUserName"]))
{
factory.UserName = configuration["EventBusUserName"];
}
if (!string.IsNullOrEmpty(configuration["EventBusPassword"]))
{
factory.Password = configuration["EventBusPassword"];
}
var retryCount = 5;
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"]))
{
retryCount = int.Parse(configuration["EventBusRetryCount"]);
}
return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount);
});
return services;
}
public static IServiceCollection AddEventBus(this IServiceCollection services, IConfiguration configuration)
{
var subscriptionClientName = configuration["SubscriptionClientName"];
services.AddSingleton<IEventBus, EventBusRabbitMQ>(sp =>
{
var rabbitMQPersistentConnection = sp.GetRequiredService<IRabbitMQPersistentConnection>();
var iLifetimeScope = sp.GetRequiredService<ILifetimeScope>();
var logger = sp.GetRequiredService<ILogger<EventBusRabbitMQ>>();
var eventBusSubcriptionsManager = sp.GetRequiredService<IEventBusSubscriptionsManager>();
var retryCount = 5;
if (!string.IsNullOrEmpty(configuration["EventBusRetryCount"]))
{
retryCount = int.Parse(configuration["EventBusRetryCount"]);
}
return new EventBusRabbitMQ(rabbitMQPersistentConnection, logger, iLifetimeScope, eventBusSubcriptionsManager, subscriptionClientName, retryCount);
});
services.AddSingleton<IEventBusSubscriptionsManager, InMemoryEventBusSubscriptionsManager>();
return services;
}
When I run the application I get the following error:
Autofac.Core.DependencyResolutionException: An exception was thrown while activating IFMS.GMT.BuildingBlocks.Infrastructure.Events.EventBusRabbitMQ.EventBusRabbitMQ -> IFMS.GMT.BuildingBlocks.Infrastructure.Events.EventBusRabbitMQ.DefaultRabbitMQPersistentConnection.
---> Autofac.Core.DependencyResolutionException: None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'IFMS.GMT.BuildingBlocks.Infrastructure.Events.EventBusRabbitMQ.DefaultRabbitMQPersistentConnection' can be invoked with the available services and parameters:
Cannot resolve parameter 'RabbitMQ.Client.IConnectionFactory connectionFactory' of constructor 'Void .ctor(RabbitMQ.Client.IConnectionFactory, Microsoft.Extensions.Logging.ILogger`1[IFMS.GMT.BuildingBlocks.Infrastructure.Events.EventBusRabbitMQ.DefaultRabbitMQPersistentConnection], Int32)'.
Autofac cant seem to find the service registered with AddCustomIntegrations()
I Moved all the code from AddCustomIntegrations() and AddEventBus() to a separate Module class that inherites from Autofac.Module class and it worked
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<InMemoryEventBusSubscriptionsManager>()
.As<IEventBusSubscriptionsManager>()
.InstancePerLifetimeScope();
builder.Register<IRabbitMQPersistentConnection>(fff =>
{
var logger = fff.Resolve<ILogger<DefaultRabbitMQPersistentConnection>>();
var factory = new ConnectionFactory()
{
HostName = Configuration["EventBusConnection"],
DispatchConsumersAsync = true
};
if (!string.IsNullOrEmpty(Configuration["EventBusUserName"]))
{
factory.UserName = Configuration["EventBusUserName"];
}
if (!string.IsNullOrEmpty(Configuration["EventBusPassword"]))
{
factory.Password = Configuration["EventBusPassword"];
}
var retryCount = 5;
if (!string.IsNullOrEmpty(Configuration["EventBusRetryCount"]))
{
retryCount = int.Parse(Configuration["EventBusRetryCount"]);
}
return new DefaultRabbitMQPersistentConnection(factory, logger, retryCount);
});
}
I have console app, where I have console app project and class library
I create appSettings.json file, where I store all data.
In console app I create this code in Program.cs to work with envVariables
class Program
{
public static IConfigurationRoot Configuration;
private static ServiceThread _serviceThread;
static async Task Main(string[] args)
{
MainAsync(args).Wait();
// Run with console or service
var asService = !(Debugger.IsAttached || args.Contains("--console"));
var builder = new HostBuilder()
.ConfigureServices((hostContext, services) => { services.AddHostedService<MonitoringService>(); });
builder.UseEnvironment(asService ? EnvironmentName.Production : EnvironmentName.Development);
if (asService)
{
await builder.RunAsServiceAsync();
}
else
{
_serviceThread = new ServiceThread();
_serviceThread.Start("Started");
await builder.RunConsoleAsync();
}
}
static async Task MainAsync(string[] args)
{
// Create service collection
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
// Create service provider
IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
// Print connection string to demonstrate configuration object is populated
Console.WriteLine(Configuration.GetConnectionString("DataConnection"));
}
private static void ConfigureServices(IServiceCollection serviceCollection)
{
// Build configuration
Configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetParent(AppContext.BaseDirectory).FullName)
.AddJsonFile("appSettings.json", false)
.Build();
// Add access to generic IConfigurationRoot
serviceCollection.AddSingleton<IConfigurationRoot>(Configuration);
}
}
Now in class library I want to work with those variables.
I tried like this
public class HelpersAppService
{
private readonly IConfigurationRoot _configuration;
public HelpersAppService(IConfigurationRoot configuration)
{
_configuration = configuration;
}
public ServerUrlsDto GetServerUrls()
{
var serverUrls = _configuration.GetSection("ServerUrls").Get<ServerUrlsDto>();
return serverUrls;
}
public AuthDto GetAuth()
{
var authData = _configuration.GetSection("Auth").Get<AuthDto>();
return authData;
}
}
But problem, that I have null configuration in this method. What I'm doing wrong?
The .NET Core framework provides many helpful extensions for you. I would suggest using them like this:
static async Task Main(string[] args)
{
// Run with console or service
var asService = !(Debugger.IsAttached || args.Contains("--console"));
var builder = Host
.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) => config
.SetBasePath(Directory.GetParent(AppContext.BaseDirectory).FullName)
.AddJsonFile("appSettings.json", false))
.ConfigureServices((context, services) => services
.AddSingleton<HelpersAppService>()
.AddHostedService<MonitoringService>())
.UseEnvironment(asService ? EnvironmentName.Production : EnvironmentName.Development);
if (asService)
{
await builder.RunAsServiceAsync();
}
else
{
_serviceThread = new ServiceThread();
_serviceThread.Start("Started");
await builder.RunConsoleAsync();
}
}
Update:
You will also need to inject an IConfiguration instead of an IConfigurationRoot like this:
private readonly IConfiguration _configuration;
public HelpersAppService(IConfiguration configuration)
{
_configuration = configuration;
}
Note:
You need to also add the HelpersAppService in the ConfigureServices method for it to be part of DI and have the IConfiguration available.
I am developing asp.net core 2.0 webapi and want a background task to process message from kafka message bus. I read some document realted to IHostedService and created a custom Background service. I am implementing CQRS with MediatR.
I have registered the MediatR module in Autofac. I need the Meditatr object to be available in the custom Hosted service. Can anyone please help me how to achieve this?
autofac.json
{
"modules": [
{
"type": "Producer.Infrastructure.Modules.MediatRModule",
"properties": {
}
}
]
}
Autofac module:
namespace Producer.Infrastructure.Modules
{
using Autofac;
using Autofac.Features.Variance;
using Producer.Application.Commands.Blogs;
using MediatR;
using System.Collections.Generic;
using System.Reflection;
public class MediatRModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterSource(new ContravariantRegistrationSource());
builder
.RegisterType<Mediator>()
.As<IMediator>()
.InstancePerLifetimeScope();
builder
.Register<SingleInstanceFactory>(ctx => {
var c = ctx.Resolve<IComponentContext>();
return t => { object o; return c.TryResolve(t, out o) ? o : null; };
})
.InstancePerLifetimeScope();
builder
.Register<MultiInstanceFactory>(ctx => {
var c = ctx.Resolve<IComponentContext>();
return t => (IEnumerable<object>)c.Resolve(typeof(IEnumerable<>).MakeGenericType(t));
})
.InstancePerLifetimeScope();
builder.RegisterAssemblyTypes(typeof(CreateBlogCommand).GetTypeInfo().Assembly).AsImplementedInterfaces(); // via assembly scan
}
}
}
program.cs
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureAppConfiguration((builderContext, config) =>
{
IHostingEnvironment env = builderContext.HostingEnvironment;
config.AddJsonFile("autofac.json");
})
.ConfigureServices(services => services.AddAutofac())
.Build();
}
}
Startup.cs
IServiceProvider serviceProvider;
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var brokerList = Configuration.GetSection("Kafka").GetValue<string>("BrokerList");
var topic = Configuration.GetSection("Kafka").GetValue<string>("Topic");
//Add framework services
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
});
services.AddSingleton<IHostedService>(s => new BackgroundService(brokerList, topic));
// Create an Autofac Container and push the framework services
var containerBuilder = new ContainerBuilder();
containerBuilder.Populate(services);
//Register your own services within Autofac
containerBuilder.RegisterModule(new ConfigurationModule(Configuration));
var container = containerBuilder.Build();
serviceProvider = container.Resolve<IServiceProvider>();
return serviceProvider;
}
Background service
public class BackgroundService : HostedService
{
public readonly string brokerList;
public readonly string topic;
public BackgroundService(string brokerList, string topic)
{
this.brokerList = brokerList;
this.topic = topic;
}
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
//I need to access the Mediatr here???
}
}
}
Thanks
There are lots of articles talking about how to use Structure Map with ASP.NET Core, but not very many talking about console applications or windows services. The default behavior in ASP.Net Core is that StructureMap creates a Nested Container per HTTPRequest so that a concrete class will be instantiated only once per HTTP Request.
I am creating a .Net Core Windows Service using the PeterKottas.DotNetCore.WindowsService nuget package. I setup StructureMap using this article: https://andrewlock.net/using-dependency-injection-in-a-net-core-console-application/
My windows service is setup on a Timer and performs an action every X number of seconds. I want each of these actions to use a nested container similar to how ASP.NET does it. In other words, I want everything created for polling pass #1 to be disposed of once that polling pass completes. When polling pass #2 starts I want all new instances of objects to be instantiated. However, within the scope of a single polling pass I only want one instance of each object to be created.
What is the proper way to do this?
Here is my program class
public class Program
{
public static ILoggerFactory LoggerFactory;
public static IConfigurationRoot Configuration;
static void Main(string[] args)
{
var applicationBaseDirectory = AppContext.BaseDirectory;
string environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
if (string.IsNullOrWhiteSpace(environment))
throw new ArgumentNullException("Environment not found in ASPNETCORE_ENVIRONMENT");
ConfigureApplication(applicationBaseDirectory, environment);
var serviceCollection = ConfigureServices();
var serviceProvider = ConfigureIoC(serviceCollection);
ConfigureLogging(serviceProvider);
var logger = LoggerFactory.CreateLogger<Program>();
logger.LogInformation("Starting Application");
ServiceRunner<IWindowsService>.Run(config =>
{
var applicationName = typeof(Program).Namespace;
config.SetName($"{applicationName}");
config.SetDisplayName($"{applicationName}");
config.SetDescription($"Service that matches Previous Buyers to Vehicles In Inventory to Fine Upgrade Opportunities.");
config.Service(serviceConfig =>
{
serviceConfig.ServiceFactory((extraArgs, microServiceController) =>
{
return serviceProvider.GetService<IWindowsService>();
});
serviceConfig.OnStart((service, extraArgs) =>
{
logger.LogInformation($"Service {applicationName} started.");
service.Start();
});
serviceConfig.OnStop((service =>
{
logger.LogInformation($"Service {applicationName} stopped.");
service.Stop();
}));
serviceConfig.OnError(error =>
{
logger.LogError($"Service {applicationName} encountered an error with the following exception:\n {error.Message}");
});
});
});
}
private static void ConfigureApplication(string applicationBaseDirectory, string environment)
{
Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
var builder = new ConfigurationBuilder()
.SetBasePath(applicationBaseDirectory)
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{environment}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
private static IServiceCollection ConfigureServices()
{
var serviceCollection = new ServiceCollection().AddLogging().AddOptions();
serviceCollection.AddDbContext<JandLReportingContext>(options => options.UseSqlServer(Configuration.GetConnectionString("JandLReporting")), ServiceLifetime.Transient);
//serviceCollection.AddDbContext<JLMIDBContext>(options => options.UseSqlServer(Configuration.GetConnectionString("JLMIDB")), ServiceLifetime.Scoped);
serviceCollection.Configure<TimerSettings>(Configuration.GetSection("TimerSettings"));
serviceCollection.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
return serviceCollection;
}
private static IServiceProvider ConfigureIoC(IServiceCollection serviceCollection)
{
//Setup StructureMap
var container = new Container();
container.Configure(config =>
{
config.Scan(scan =>
{
scan.AssemblyContainingType(typeof(Program));
scan.AssembliesFromApplicationBaseDirectory();
scan.AssembliesAndExecutablesFromApplicationBaseDirectory();
scan.WithDefaultConventions();
});
config.Populate(serviceCollection);
});
return container.GetInstance<IServiceProvider>();
}
private static void ConfigureLogging(IServiceProvider serviceProvider)
{
LoggerFactory = serviceProvider.GetService<ILoggerFactory>()
.AddConsole(Configuration.GetSection("Logging"))
.AddFile(Configuration.GetSection("Logging"))
.AddDebug();
}
}
Here is my WindowsService class:
public class WindowsService : MicroService, IWindowsService
{
private readonly ILogger _logger;
private readonly IServiceProvider _serviceProvider;
private readonly TimerSettings _timerSettings;
public WindowsService(ILogger<WindowsService> logger, IServiceProvider serviceProvider, IOptions<TimerSettings> timerSettings)
{
_logger = logger;
_serviceProvider = serviceProvider;
_timerSettings = timerSettings.Value;
}
public void Start()
{
StartBase();
Timers.Start("ServiceTimer", GetTimerInterval(), async () =>
{
await PollingPassAsyc();
},
(error) =>
{
_logger.LogCritical($"Exception while starting the service: {error}\n");
});
}
private async Task PollingPassAsyc()
{
using (var upgradeOpportunityService = _serviceProvider.GetService<IUpgradeOpportunityService>())
{
await upgradeOpportunityService.FindUpgradeOpportunitiesAsync();
}
}
private int GetTimerInterval()
{
return _timerSettings.IntervalMinutes * 60 * 1000;
}
public void Stop()
{
StopBase();
_logger.LogInformation($"Service has stopped");
}
}
There is extension method CreateScope for IServiceProvider in Microsoft.Extensions.DependencyInjection namespace. What it does is resolve special interface (IServiceScopeFactory) from current DI container, which is responsible for creating new scopes, and creates new scope using this factory. StructureMap registers implementation of this interface, so when you call CreateScope - StructureMap will create nested container. Sample usage:
using (var scope = _serviceProvider.CreateScope()) {
// use scope.ServiceProvider, not _serviceProvider to resolve instance
var service = scope.ServiceProvider.GetService<IUpgradeOpportunityService>();
}