Skip to content

Latest commit

 

History

History
88 lines (65 loc) · 2.74 KB

File metadata and controls

88 lines (65 loc) · 2.74 KB

XPC3004: Do not use LocalPluginContext as TService in RegisterStep

Severity

Error

Description

This rule reports when LocalPluginContext is used as the TService type argument in RegisterStep<TEntity, TService>(). This causes a runtime exception because LocalPluginContext is not registered in the DI container. At runtime, the plugin will call sp.GetRequiredService<LocalPluginContext>(), which throws InvalidOperationException.

This typically happens when migrating from the legacy RegisterPluginStep<T> API and mistakenly using RegisterStep<TEntity, LocalPluginContext> instead of the correct DI-based approach.

❌ Example of violation

public class ContactPlugin : Plugin
{
    public ContactPlugin()
    {
        // XPC3004: LocalPluginContext is not in DI — causes runtime exception
        RegisterStep<Contact, LocalPluginContext>(
            EventOperation.Update,
            ExecutionStage.PostOperation,
            Execute);
    }

    private void Execute(LocalPluginContext context) { }
}

✅ How to fix (interim — keep LocalPluginContext logic)

Use RegisterPluginStep<T> which correctly wraps the LocalPluginContext:

public class ContactPlugin : Plugin
{
    public ContactPlugin()
    {
        RegisterPluginStep<Contact>(
            EventOperation.Update,
            ExecutionStage.PostOperation,
            Execute);
    }

    private void Execute(LocalPluginContext context) { }
}

✅ How to fix (recommended — migrate to DI service)

Migrate to a DI-registered service type for full dependency injection support:

public class ContactPlugin : Plugin
{
    public ContactPlugin()
    {
        RegisterStep<Contact, IContactService>(
            EventOperation.Update,
            ExecutionStage.PostOperation,
            nameof(IContactService.HandleUpdate));
    }

    protected override IServiceCollection OnBeforeBuildServiceProvider(IServiceCollection services)
    {
        return services.AddScoped<IContactService, ContactService>();
    }
}

public interface IContactService
{
    void HandleUpdate();
}

Why this matters

RegisterStep<TEntity, TService> calls sp.GetRequiredService<TService>() at runtime. LocalPluginContext is constructed manually (not registered in the DI container), so this call throws InvalidOperationException. The exception is then caught and re-thrown as InvalidPluginExecutionException, masking the original cause.

Code fix availability

Yes — the code fix rewrites RegisterStep<TEntity, LocalPluginContext>(...) to RegisterPluginStep<TEntity>(...).

See also