Take the community feedback survey now.
Take the community feedback survey now.
 
                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?
