I will try to be very clear.

I am writing a Queue Azure Function in .net7.

I have some Managers that implements an interface. I know which manager instanciate only when a new message arrive. My managers have only one constructor with an interface in input:

internal class MyNotificationManager : INotificationManager
{
    public MyNotificationManager(IContext context)
    { ... }

      ...
}

Here my Main, where I configure all my services:

public static void Main()
{
    string connectionString = System.Environment.GetEnvironmentVariable("DatabaseConnectionString");

    var host = new HostBuilder()
        .ConfigureFunctionsWorkerDefaults()
        .ConfigureServices(s =>
        {
            s.AddTransient<IContext, MyDBContext>(c => new MyDBContext(connectionString));
            s.AddTransient<INotificationManagerResolver, NotificationManagerResolver>();
            ...
        })
        .Build();

    host.Run();
}

The NotificationManagerResolver that you see above creates the instance of one of my mager. Here the implementation:

internal class NotificationManagerResolver : INotificationManagerResolver
{
    public INotificationManager Resolve(string notificationType)
    {
        ...

        var type = Assembly.GetAssembly(typeof(NotificationManagerResolver))
                           .GetType(concreteType);
            
        var instance = Activator.CreateInstance(type);

        return instance as INotificationManager;
    }
}

In the function, I try to resolve the manager:

    private readonly INotificationManagerResolver _notificationManagerResolver;

    ...

    [Function("MyFunction")]
    public async Task Run([QueueTrigger("%QueueName%", Connection = "StorageConnectionString")] string queueItem)
    {
        var notificationManager = _notificationManagerResolver.Resolve(c.NotificationType);
    }

Obviously I get an error when I try to resolve and instanciate my manager, because it does not know how resolve the IContext in input in the constructor of the manager, even though I defined the mapping in the Main.

I can easily resolve the problem removing the line

s.AddTransient<IContext, MyDBContext>(c => new MyDBContext(connectionString));

from the Main and modifying the resolver. If I change the line

var instance = Activator.CreateInstance(type);

with this line

var instance = Activator.CreateInstance(type, new MyDBContext(connectionString));

everything works correctly.

But I don't like the solution. I do not want to bind the resolver to the definition of IContext.

Any suggestion please?

Thank you

1

There are 1 best solutions below

0
Serg On BEST ANSWER

If you want to register all existing implementation of INotificationManager without explicitly listing all its types in the code, you can perform the assembly scan and find all implementations at runtime. Then use DI container to resolve the exact implementation you need. To do so:

  1. Add scrutor nuget-package to support assembly scan capabilities.

  2. Modify your registration code in the following way scan assembly, containing the NotificationManagerResolver type for types that implements INotificationManager interface

     var host = Host.CreateDefaultBuilder(args)
         .ConfigureServices(s =>
         {
             s.AddTransient<IContext, MyDBContext>(c => new MyDBContext(connectionString));
             s.AddTransient<INotificationManagerResolver, NotificationManagerResolver>();
             s.Scan(scan => scan.FromAssemblyOf<NotificationManagerResolver>()
                 .AddClasses(classes => classes.AssignableTo<INotificationManager>())
                 .AsSelf()
                 .WithTransientLifetime());
    
    
         })
         .Build();
    
  3. Modify NotificationManagerResolver to get service provider as dependency and then use it to resolve managers

     public class NotificationManagerResolver : INotificationManagerResolver
     {
         private readonly IServiceProvider _provider;
    
         public NotificationManagerResolver(IServiceProvider provider)
         {
             _provider = provider;
         }
    
         public INotificationManager? Resolve(string notificationType)
         {
             var concreteType = GetManagerTypeForNotification(notificationType);
             return _provider.GetService(concreteType) as INotificationManager;
         }
     }