Configuring Rebus in a .net core Worker Service (or a Console App) - .net-core

I have seen that Adding rebus in the ASP.NET Core execution pipeline is very neat using Startup.cs.
I wonder if there is a same neat way to do the same for Worker service or generally a console app.
Most .net core console apps I have seen are very simple demo applications.
Kindly if there is any concrete sample configuration using .net core console application.
Regards
Amour Rashid

One way would be to add Microsoft's Microsoft.Extensions.Hosting package and build your background service as a BackgroundService:
public class MyBackgroundService : BackgroundService
{
readonly IServiceCollection _services = new ServiceCollection();
public BackgroundService()
{
// configure the bus
services.AddResbus(
configure => configure
.Transport(t => t.Use(...))
);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using var provider = _services.BuildServiceProvider();
// start the bus
provider.UseRebus();
while (!stoppingToken.IsCancellationRequested)
{
await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken);
}
}
}
which you then add by going
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<MyBackgroundService>();
});
in your startup.

Thanks Mogens,
Another way is to
var host =CreateHostBuilder(args).Build();
host.UseRebus();
host.Run();
Or in ConfigureServices method
....
var provider=services.CreateServiceProvider();
provider.UseRebus();
It helped me I could create Worker Services using rebus.

Related

Adding MassTransit Consumer to a Asp.net Framework Web application

I am Tring to add MassTransit Consumer to a Asp.net Framework (.net 4.8) Web application. I not see any sample that use .net framework , all sample point to .NET Core .
Is this Supported by MassTransit in .net Framework (.net 4.8). Is this Long running calls will be killed Off by IIS ?
My sample Code for Global.asax.cs
public class MvcApplication : System.Web.HttpApplication
{
static IBusControl _bus;
static BusHandle _busHandle;
public static IBus Bus
{
get { return _bus; }
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
_bus = MassTransit.Bus.Factory.CreateUsingRabbitMq(cfg =>
{
cfg.Host(new Uri(ConfigurationManager.AppSettings["RabbitMQHost"]), h =>
{
h.Username("admin");
h.Password("admin");
});
cfg.ReceiveEndpoint(RA_Listener.RA_Listener0, e =>
{
e.Consumer<EventConsumer>();
});
});
_busHandle = MassTransit.Util.TaskUtil.Await<BusHandle>(() => _bus.StartAsync());
}
It's only a long-running call if the bus can't connect to your message broker. Otherwise, it should complete almost immediately. So if your broker is unreachable, that's likely the issue.
Also, you should be configuring an ILoggerFactory and providing it to MassTransit if you expect to see any useful log output to help you troubleshoot any issues. Configure logging using LogContext.ConfigureCurrentLogContext(loggerFactory); within the bus configuration closure.
You could just create the bus, as you've done, and use:
Task.Run(() => _bus.StartAsync());
It won't wait, but you also won't know if the bus actually started since you aren't observing the result of the task. But, at least it won't block your startup.

How to run sychronous tasks on IHost start up

I am working with Windows Service that is a .Net Core Console application.
It contains an instance of an IHostedService which reads messages from a queue and processes them.
Before reading the messages I want to do a short database call and populate a cache.
How do I prevent the hosted service from starting until I have finished populating my cache?
The hostbuilder is currently configured like this:
public static void Main(string[] args)
{
var hostBuilder = Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureServices((context, services) =>
{
services
.AddHostedService<MessageProcessor>()
.AddSingleton(provider => new ServiceBusClient(_connectionString))
.AddSingleton<ICachePopulator, CachePopulator>()
});
hostBuilder.Build().Run();
}
I looked at the IStartupFilter interface but it seems to be suitable for only ASP.Net Core services (please correct me if I'm wrong here).
I also looked at the Startup class but it seems to be suitable for configuration only rather than synchronous tasks.

Blazor Hosting Startup Assembly not being recognized

I am building a Blazor app (both wasm and server) which both share an API and a set of Services. I have the services broken out into its own class library. There are probably 50 or so services and I dont want to duplicate the service declarations in the Server and WASM configuration sections.
Current Situation
WASM
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
var zooAPI = new Uri("http://localhost:51552/api/v1/");
builder.Services.AddHttpClient<IZooService, ZooService>(client => client.BaseAddress = zooAPI);
await builder.Build().RunAsync();
}
Server
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
var zooAPI = new Uri("http://localhost:51552/api/v1/");
services.AddHttpClient<IZooService, ZooService>(client => client.BaseAddress = zooAPI);
}
Since both of these register the services which will end up being over 50 services id like to add a Startup within my Services Class Library.
[assembly: HostingStartup(typeof(Zoo.Services.ServicesStartup))]
namespace Zoo.Services
{
public class ServicesStartup : IHostingStartup
{
public void Configure(IWebHostBuilder builder)
{
var zooAPI = new Uri("http://localhost:51552/api/v1/");
builder.ConfigureServices((context, services) =>
{
services.AddHttpClient<IZooService, ZooService>(client => client.BaseAddress = zooAPI);
});
}
}
}
The issue I have is that this Startup is not being recognized and the Services are not being registered. The exception is "An Unhandled exception occured while processing the request. InvalidOperationException: Cannot provide a value for property 'ZooService'. There is no registered service of type IZooService.
What am I missing to have this ServiceStartup recognized and the registered upon app start?
In order for the Blazor app to know there is a HostingStartup it needs to read you need to tell it the assemblies to look into.
For Server Hosting in the Program.cs you can add the StartupAssemblies Keys or you can add it to the enviornmentvariables ASPNETCORE_HOSTINGSTARTUPASSEMBLIES
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, "Zoo.Services").UseStartup<Startup>();
});
I have not figured out how to add it to the WASM yet

Alternative code for services.BuildServiceProvider (in ConfigureServices) when migrating from ASP.NET Core 2.2 to 3.1

I have modular ASP.NET Core application. Back in the day of .NET Core 1.1 I build services (AddScoped, AddSingleton ...) in public void ConfigureServices(IServiceCollection services) method like this:
public void ConfigureServices(IServiceCollection services)
{
//...
var sp = services.BuildServiceProvider();
GlobalConfiguration.ModuleInitializers = sp.GetServices<IModuleInitializer>();
foreach (var moduleInitializer in GlobalConfiguration.ModuleInitializers)
moduleInitializer.ConfigureServices(services, this.Configuration, this.Environment);
}
each module (library) has own class which implements 'IModuleInitializer' and register services:
public class ModuleInitializer : IModuleInitializer
{
public void ConfigureServices(IServiceCollection serviceCollection, IConfiguration configuration, IHostingEnvironment environment)
{
serviceCollection.AddScoped<....>();
serviceCollection.AddScoped<...>();
...
With migration from .NET Core 2.2 to 3.1 I am receiving warning:
Calling 'BuildServiceProvider' from application code results in an
additional copy of singleton services being created. Consider
alternatives such as dependency injecting services as parameters to
'Configure'
I already educate myself what that means, where is the problem ..., but I am having problems how to properly configure services in a module design application.
I also need to get information about modules from internet. My question is if this approach is good, or is there any better/recommended?
public Startup(IConfiguration configuration)
{
Configuration = configuration; //From https://learn.microsoft.com/en-us/aspnet/core/fundamentals/startup?view=aspnetcore-3.1
}
public void ConfigureServices(IServiceCollection services)
{
...
var serverSett = this.Configuration.GetSection("Server").Get<ServerSettings>();
string[] allowedModules;
using (var httpClientHandler = new HttpClientHandler())
{
allowedModules = //Get allowed modules from web
}
if (allowedModules.Any(t => t == "Shopping");
services.AddShopping(); //Extension method in Shopping library which register own services.
if (allowedModules.Any(t => t == "Offer");
services.AddOffer(); //Extension method in Offer library which register own services.
}
Using the DI to setup the DI is a bit of a problem and could definitely be problematic.
Why not just use some reflection to get the IModuleInitializers.
var moduleInitializers = typeof(MyAssembly).GetTypeInfo().Assembly
.GetTypes()
.Where(type =>
!type.GetTypeInfo().IsAbstract &&
typeof(IModuleInitializer).IsAssignableFrom(type))
.Select(type => Activator.CreateInstance(type))
.Cast<IModuleInitializer>()
.ToArray();
foreach (var moduleInitializer in moduleInitializers)
moduleInitializer.ConfigureServices(services, this.Configuration, this.Environment);
As for your second question I cannot give you an advice. But I would question the need to dynamically setup the app unless you really really need that.

Hangfire recurring job at a specific time ASP MVC

Ive been recently using hangfire to process lengthy jobs enabling me to return API calls more efficiently within an ASP MVC Core application.
i have implimented this by adding the following to startup.cs
public class Startup
{
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddCors(options => options.AddPolicy("AllowAll", p => p.AllowAnyOrigin()));
services.AddHangfire(configuration => configuration.UseSqlServerStorage(Configuration.GetConnectionString("DefaultConnection")));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseCors("AllowAll");
app.UseMvc();
app.UseHangfireDashboard();
app.UseHangfireServer();
}
and then calling this within an action on a controller
using (new BackgroundJobServer())
{
formData.DateCreated = DateTime.Now;
formData.Source = "Web";
formData.StatusItem = _context.Status.FirstOrDefault(x => x.Default == true);
_context.Lead.Add(formData);
_context.SaveChanges();
}
Now i have a need to send a an email at 1am every day with the status of records from the database.
Im slightly confused on the implementation from the following perspective:
-Where do i implement the background job?
-Do i put the call in a method, if so how is this called/
-I cannot find BackgroundJob.AddOrUpdate, which i understand is used for the recurring tasks
-The schedule method takes a timespan object, but all examples are using CRON.
I have all the code to create the email, i just need help scheduling it
Thanks for your help in advance.

Resources