Search code examples
c#asp.net-coredependency-injectioninversion-of-controlasp.net-core-5.0

Register IStartupFilter with TryAddSingleton equivalent in ASP.NET Core


My class library registers an IStartupFilter (using a factory) with the ASP.NET Core container:

services.AddSingleton<IStartupFilter>(serviceProvider => 
  ActivatorUtilities.CreateInstance<MyStartupFilter>(serviceProvider, new object[] { arg1, arg2 }));

That works.

But there are multiple ways to register the class library, so I don't want the filter to be added multiple times.

  • If I use AddSingleton then the service is added, but could be added multiple times.
  • If I use TryAddSingleton then the service is not added at all - because the runtime already registers an IStartupFilter of its own (HostFilteringStartupFilter)

How can I "try add" a startup filter that uses a factory?


Solution

  • Here's a dirty way to do it. If there's a better way please post your answer and I'll accept it.

    var alreadyRegistered = services.Any(sd => 
      sd.ServiceType == typeof(IStartupFilter) &&
      sd.ImplementationFactory?.Target?.GetType() == this.GetType()
    );
    
    if (!alreadyRegistered)
    {
      services.AddSingleton<IStartupFilter>(serviceProvider => 
        ActivatorUtilities.CreateInstance<MyStartupFilter>(serviceProvider, new object[] { arg1, arg2 }));
    }
    

    This fiddles with the service collection, which is an implementation detail of the container that should be left alone. And it assumes that the check and the registration occur in the same class. And it does not actually specify MyStartupFilter, so you're not even sure it's doing the right thing, especially if that class adds other startup filters.

    Dirty but works in simple cases.