Ok, so the line above works in a Controller.
I was trying to run this in an extension method for a plugin/addon/module in startup.cs, after .AddCms(). How can I hook up content events for an addon, now that I cannot use the InitializationModule anymore...?
You can still use them, but for (startup) performance reasons we should limit the number of initialization modules.
But you could do something like the following instead:
services.Configure<IContentEvents>(x => x.PublishedContent += OnPublishedContent);
Edit: Ok, I could have sworn the above code worked before, I must be confused.
Here's a quick workaround without having to add an initialization module (for ConfigureServices in Startup):
var serviceProvider = services.BuildServiceProvider();
var contentEvents = serviceProvider.GetService<IContentEvents>();
contentEvents.PublishedContent += OnPublishedContent;
As .NET events need to be attached to an instance and the ConfigureServices
method is called before any instances are created, the best place to do this in an Web Application would probably be in the Configure
method of your Startup
class. To avoid the ServiceLocator
, you can declare IContentEvents
as an argument of the Configure
method.
public void Configure(IApplicationBuilder app, IContentEvents contentEvents)
{
contentEvents.PublishedContent += OnPublishedContent;
// ...
}
If you don't have access to the Startup class, for example when building a reusable module, and you want to avoid creating an InitializableModule, you can create a class that implements Microsoft.Extensions.Hosting.IHostedService
and attach your event handlers in the StartAsync method. Then let the Application register this service in the ConfigureServices
method. This is essentially how the Initialize method of initialization modules are called. Hosted services are started just before the Configure
method is called.
internal class ContentEventsSubscriber : IHostedService
{
private readonly IContentEvents _contentEvents;
public ContentEventsSubscriberIContentEvents contentEvents)
{
_contentEvents = contentEvents;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_contentEvents.PublishedContent += OnPublishedContent;
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_contentEvents.PublishedContent -= OnPublishedContent;
return Task.CompletedTask;
}
}
It seems the ServiceLocator.Current at that time has not been created, and the value is null. You have two options here
I personally will go #2 approach, as #1 used to be considered as code smell. Manually invoking this could lead unexpected behaviour (e.g. intensive singleton services are being re-created)
Is there anything new with ContentEvents, registering of Optimizely types, or ServiceLocation in CMS 12 that I'm missing?