Skip to content

Trigger registration

Koen edited this page Nov 29, 2020 · 7 revisions

Triggers are registered either with the DbContext- or with a dependency injection container. Assuming that you're using DI:

services.AddTransient<IBeforeSaveTrigger<Student>, AssignDefaultCreatedDate>();

This will ensure that each individual trigger invocation uses a new instance of AssignDefaultCreatedDate. If you want to share state amongst trigger invocations you can use Scoped or Singleton registrations. Scoped triggers are scoped on the current ServiceProvider which is typically managed by the calling code.

What if you want to implement multiple triggers? Lets say we have a trigger as such:

public class RemoveEmailAddressesFromCommentContent : IBeforeSaveTrigger<Comment>, IAfterSaveTrigger<Comment>
{
    private List<string> _foundEmailAddresses;

    public Task BeforeSave(ITriggerContext<BlogPost> context, CancellationToken cancellationToken)
    {
        if (context.ChangeType is ChangeType.Added or ChangeType.Modified)
        {
            _foundEmailAddresses = Helpers.FindAndReplaceEmailAddresses(context.Entity.Content);
        }
    }

    public Task AfterSave(ITriggerContext<BlogPost> context, CancellationToken cancellationToken)
    {
        if (_foundEmailAddresses != null) 
        {
            _emailSevice.SendEmail(context.AuthorEmailAddress, "For your protectection, we've removed the following email addresses: " + string.Join(", ", _foundEmailAddresses));
        }
    }
}

State needs to be shared between the BeforeSave and AfterSave trigger. This can be accomplished by registering them with your DI container:

services.AddScoped<RemoveEmailAddressFromCommentContent>();
services.AddScoped<IBeforeSaveTrigger<Comment>>(sp => sp.GetService<RemoveEmailAddressFromCommentContent>());
services.AddScoped<IAfterSaveTrigger<Comment>>(sp => sp.GetService<RemoveEmailAddressFromCommentContent>());

Sometimes you just want to register all triggers automatically, this can be accomplished with a little helper method:

void RegisterAssemblyTriggers(Type genericTriggerType)
{
    var triggerCandidates = typeof(ServiceCollectionExtensions)
        .Assembly
        .GetTypes()
        .Where(x => x.IsClass);

    foreach (var triggerCandidate in triggerCandidates)
    {
        foreach (var @interface in triggerCandidate.GetInterfaces())
        {
            if (@interface.IsConstructedGenericType && @interface.GetGenericTypeDefinition() == genericTriggerType)
            {
                services.TryAddScoped(triggerCandidate);

                services.AddScoped(@interface, serviceProvider => serviceProvider.GetRequiredService(triggerCandidate));
            }
        }
    }
}

RegisterAssemblyTriggers(typeof(IBeforeSaveTrigger<>));
RegisterAssemblyTriggers(typeof(IAfterSaveTrigger<>));
RegisterAssemblyTriggers(typeof(IAfterSaveFailedTrigger<>));

Clone this wiki locally