Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error on handling AddSingletonIfNotExists (AddApplicationInsightsTelemetryWorkerService) #2921

Open
JJong-nl opened this issue Dec 5, 2024 · 3 comments

Comments

@JJong-nl
Copy link

JJong-nl commented Dec 5, 2024

I have a service on the ServiceCollection that has no ImplementationType and no ImplementationInstance.
It is a KeyedImplementationType

internal static void AddSingletonIfNotExists<TService, TImplementation>(this IServiceCollection services)
where TService : class
where TImplementation : class, TService
{
if (!services.Any(o => o.ImplementationFactory == null && typeof(TImplementation).IsAssignableFrom(o.ImplementationType ?? o.ImplementationInstance.GetType())))
{
services.AddSingleton<TService, TImplementation>();
}
}

Than the ImplementationInstance.GetType() returns an error.
System.NullReferenceException: 'Object reference not set to an instance of an object.'

The problem is that the DiagnosticsTelemetryModule is not registred to the ServiceProvider.

repro ..
use Scrutor >=5.0.2 for adding decorators
add services
add decorators for specific open generic interfaces. cq services.Decorate(typeof(IMessageHandler<>), typeof({yourdecoratortype<>}));

services.AddHostedService<ChannelWorker>();
services.AddApplicationInsightsTelemetryWorkerService();
if (services.Where(o=>o.ImplementationType == typeof(DiagnosticsTelemetryModule)).Count() == 0)
{
    throw new Exception("There is no DiagnosticsTelemetryModule registred.");
}
@JJong-nl
Copy link
Author

JJong-nl commented Dec 5, 2024

<PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.22.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Scrutor" Version="5.0.2" />
using Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing;
using Test;

var builder = Host.CreateApplicationBuilder(args);

builder.Services.AddServices(builder.Configuration);

var host = builder.Build();
host.Run();


namespace Test
{
    public static class Module
    {
        internal static IServiceCollection AddServices(this IServiceCollection services, IConfiguration configuration)
        {
            // <-- Here we can use AddApplicationInsightsTelemetryWorkerService()

            // register all handlers
            services.Scan(scan =>
                scan.FromAssemblies(typeof(Module).Assembly)
                .AddClasses(classes => classes.AssignableTo(typeof(IMessageHandler<>)).Where(t => !t.IsGenericType))
                .AsImplementedInterfaces(t => t.GetGenericTypeDefinition() == typeof(IMessageHandler<>))
                .WithTransientLifetime());

            // <-- Here we can use AddApplicationInsightsTelemetryWorkerService()

            services.Decorate(typeof(IMessageHandler<>), typeof(MessageHandlerDecorator<>));

            // <-- Here we can NOT use AddApplicationInsightsTelemetryWorkerService() ##<##<##<#########################
            services.AddApplicationInsightsTelemetryWorkerService();

            if (services.Where(o => !o.IsKeyedService && o.ImplementationType == typeof(DiagnosticsTelemetryModule)).Count() == 0)
            {
                throw new Exception("There is no DiagnosticsTelemetryModule registred.");
            }

            services.AddHostedService<Worker>();
            return services;
        }
    }

    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;

        public Worker(ILogger<Worker> logger)
        {
            _logger = logger;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                // ..
                // handling messages
                _logger.LogInformation("Starting succes");
                await Task.Delay(1000, stoppingToken);
            }
        }
    }

    public interface IMessageHandler<T> where T : BaseMessage
    {
        Task HandleAsync(T message, CancellationToken cancellationToken);
    }

    public class BaseMessage
    {
        public string Value { get; set; } = "";
    }

    public class MessageHandler : IMessageHandler<BaseMessage>
    {
        private readonly ILogger<MessageHandler> _logger;
        private readonly IConfiguration _configuration;

        public MessageHandler(ILogger<MessageHandler> logger, IConfiguration configuration)
        {
            _logger = logger;
            _configuration = configuration;
        }

        public async Task HandleAsync(BaseMessage message, CancellationToken cancellationToken)
        {
            _logger.LogInformation($"Handling: {message.Value}");

        }
    }

    public class MessageHandlerDecorator<T> : IMessageHandler<T>
        where T : BaseMessage
    {
        IMessageHandler<T> _handler;

        public MessageHandlerDecorator(IMessageHandler<T> handler)
        {
            _handler = handler;
        }

        public async Task HandleAsync(T message, CancellationToken cancellationToken)
        {
            // do some stuff for decorating..
            await _handler.HandleAsync(message, cancellationToken);
        }
    }
}

Solution is adding !o.IsKeyedService

        internal static void AddSingletonIfNotExists<TService, TImplementation>(this IServiceCollection services)
            where TService : class
            where TImplementation : class, TService
        {
            if (!services.Any(o => o.ImplementationFactory == null && !o.IsKeyedService && typeof(TImplementation).IsAssignableFrom(o.ImplementationType ?? o.ImplementationInstance.GetType())))
            {
                services.AddSingleton<TService, TImplementation>();
            }
        }

@johanbenschop
Copy link

We encountered this issue after upgrading to .NET 9, along with many other NuGet packages. At first, we got an error saying the instrumentation key was invalid, but after debugging and noticing the IDE breaking in the call to AddSingletonIfNotExists for the null reference exception mentioned and seeing down the stack in AddApplicationInsightsTelemetry() the try/catch, we figured this error was due to the incomplete services registration.

The solution was to place the services.AddApplicationInsights(); call to the top so the bug has no chance of bugging.

fail: Microsoft.ApplicationInsights.Profiler.Core.IServiceProfilerContext[0]
      Profiler Instrumentation Key is invalid.
      Microsoft.ServiceProfiler.Agent.Exceptions.InstrumentationKeyInvalidException: Instrumentation Key is empty.
         at Microsoft.ServiceProfiler.Utilities.AppInsightsProfileFetcher.FetchProfileAsync(Guid iKey, Uri endpoint, Int32 retryCount, Action`1 responseCallback)
         at Microsoft.ApplicationInsights.Profiler.Core.ServiceProfilerContext.GetAppInsightsAppIdAsync()
Unhandled exception. System.InvalidOperationException: Unable to resolve service for type 'Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration' while attempting to activate 'Microsoft.ApplicationInsights.Profiler.Core.Auth.AuthTokenProvider'.

This is related to #2915.

@claudiogodoy99
Copy link

claudiogodoy99 commented Dec 13, 2024

It has been solved on the PR: #2908

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants