I am creating a generic Startup.cs in a helper library for Azure Functions v4. I am using the DependencyResolver class to resolve dependency injection. Here is the code:

using Infrastructure.DependecyInjection.LifecycleEnum;
using Infrastructure.DependecyInjection.ServiceModel;
using Infrastructure.QueryBuilder;
using Infrastructure.QueryBuilder.KataQueryBuilder;
using Infrastructure.QueryExecutor;
using Infrastructure.QueryExecutor.DapperQueryExecutor;
using Infrastructure.ReadModel;
using Microsoft.Extensions.DependencyInjection;
using Polly.Registry;
using SqlKata.Compilers;
using System.ComponentModel;


namespace Infrastructure.DependecyInjection;

public static class DependencyResolver 
{



    [Description("Resolve the dependency by considering using Sql Kata as a foundation. God Bless Chat GPT")]
    public static IServiceCollection ResolveIQueryBuilder(this IServiceCollection services, //string schema,
        object kataQueryBuilderService, Lifecycle lifecycle = Lifecycle.Scoped) //where TCompiler : Compiler, new()
    {
        var lifetime = lifecycle.GetServiceLifetime();

        services.Add(
            new ServiceDescriptor(
                 typeof(IQueryBuilder),
                 provider => kataQueryBuilderService,
                 lifetime)
            );
        return services;
    }


    public static IServiceCollection ResolveIKataQueryBuilder<TCompiler>(this IServiceCollection services, string schema
               , Lifecycle lifecycle=Lifecycle.Scoped) where TCompiler : Compiler, new()
    {
        services.Add(
           new ServiceDescriptor(typeof(IKataQueryBuilder<TCompiler>), provider => new KataQueryBuilder<TCompiler>(schema,
                  Activator.CreateInstance<TCompiler>()), lifecycle.GetServiceLifetime()));
        return services;

    }


    [Description("Resolve the dependency by using Dapper as a foundation.")]
    public static IServiceCollection ResolveIQueryCommand(this IServiceCollection services
        , Lifecycle lifecycle = Lifecycle.Singleton)
    {
        services.Add(new ServiceDescriptor(typeof(IQueryCommand), typeof(DapperQueryCommand), lifecycle.GetServiceLifetime()));
        return services;
    }


    public static IServiceCollection ResolveIReadModel<TPolicy> (this IServiceCollection services, string connectionString,
        IQueryCommand queryCommandService, IQueryBuilder queryBuilderService
        ,TPolicy policy, Lifecycle lifecycle = Lifecycle.Scoped)
        where TPolicy : IReadOnlyPolicyRegistry<string>
    {
        ServiceLifetime serviceLifetime = lifecycle.GetServiceLifetime();   
        
        services.Add(
            new ServiceDescriptor(typeof(IReadModel)
                                , provider => new ReadModel.ReadModel(
                                        connectionString, 
                                        queryCommandService,
                                        policy, 
                                        queryBuilderService
                                       )
                                    , lifecycle.GetServiceLifetime()
                       )
                );

        return services;
    }

    [Description("Debugging purpose only")]
    internal static void CheckServices(this IServiceCollection services)
    {
        var querycommand = services.BuildServiceProvider().GetRequiredService(typeof(IQueryCommand));
        var querybuilder = services.BuildServiceProvider().GetRequiredService(typeof(IQueryBuilder));
        var readmodel = services.BuildServiceProvider().GetRequiredService(typeof(IReadModel));

    }
    
    public static IServiceCollection ResolveExternalServices<TServices>(this IServiceCollection services,TServices externalServices)
        where TServices : IEnumerable<Service>
    {
        foreach(var externalService in externalServices)
        {
            services.Add(
                new ServiceDescriptor(externalService._serviceType, externalService._implementationType
                    , externalService._lifecycle.GetServiceLifetime())
                );
        }
        return services;
    }

    private static ServiceLifetime GetServiceLifetime(this Lifecycle lifecycle)
    {
        return lifecycle switch
        {
            Lifecycle.Scoped => ServiceLifetime.Scoped,
            Lifecycle.Transient => ServiceLifetime.Transient,
            _ => ServiceLifetime.Singleton
        };
    }
}

I use the following class as the base for the generic startup:


using Infrastructure.DependecyInjection;
using Infrastructure.DependecyInjection.LifecycleEnum;
using Infrastructure.DependecyInjection.ServiceModel;
using Infrastructure.QueryBuilder;
using Infrastructure.QueryBuilder.KataQueryBuilder;
using Infrastructure.QueryExecutor;
using Infrastructure.ReadModel;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Polly.Registry;
using SqlKata.Compilers;

namespace Infrastructure.GenericStartup;

public static class Startup
{
    public static IFunctionsHostBuilder UseGenericStartup<TServices>(this IFunctionsHostBuilder builder, 
        TServices services, Lifecycle lifecycle=Lifecycle.Scoped) where TServices : IEnumerable<Service>
    {
        //TODO KeyVault and AppConfig integration
        string connectionString = "MyConnectionString";
        string schema = "MySchema";

        PolicyRegistry policyRegistry = new PolicyRegistry()
        {
            { RetryPolicyNames.SQL_COMMAND_RESILIENCE, PolicyDefinitions.SqlQueryRetryPolicy }
        };

       _= builder.Services.ResolveIKataQueryBuilder<SqlServerCompiler>(schema, lifecycle);
        var kataQueryBuilderService=builder.Services.BuildServiceProvider().GetRequiredService(typeof(IKataQueryBuilder<SqlServerCompiler>));

       _= builder.Services.ResolveIQueryBuilder(kataQueryBuilderService,lifecycle);
        var queryBuilderService=builder.Services.BuildServiceProvider().GetRequiredService(typeof(IQueryBuilder));

       _= builder.Services.ResolveIQueryCommand(lifecycle);
        var queryCommandService=builder.Services.BuildServiceProvider().GetRequiredService(typeof(IQueryCommand));

       _= builder.Services.ResolveIReadModel(connectionString,(IQueryCommand)queryCommandService,
            (IQueryBuilder)queryBuilderService,policyRegistry,lifecycle);

        var readModelService=builder.Services.BuildServiceProvider().GetRequiredService(typeof(IReadModel));

      _= builder.Services.ResolveExternalServices(services);

        return builder;

    }
}

The startup of my Function is:


using AzFuncTest.ServiceProvider;
using Infrastructure.DependecyInjection.LifecycleEnum;
using Infrastructure.GenericStartup;
using Infrastructure.QueryBuilder;
using Infrastructure.QueryExecutor;
using Infrastructure.ReadModel;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;

[assembly: FunctionsStartup(typeof(AzFuncTest.Startup))]
namespace AzFuncTest;

public class Startup:FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        
        

        builder.UseGenericStartup(Services.GetServices(), Lifecycle.Scoped);


    }
}

When I run the function, it starts without any issues. However, when I make a call to the function, I get the following error:

"Microsoft.Azure.WebJobs.Script.WebHost: Registered factory delegate returns service Infrastructure.ReadModel.ReadModel is not assignable to scoped container with {no name}"

This is the function code:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Common.Global;
using Infrastructure.ReadModel;

namespace AzFuncTest;

public  class TestFunction
{


    private readonly IReadModel _readModel;

   

    public TestFunction(IReadModel readModel)
    {

        _readModel = readModel ?? throw new ArgumentNullException(nameof(readModel));

    }


    [FunctionName(GlobalCostants.TEST)]
    public  async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = GlobalCostants.TEST+ "/MaterialCode/{materialCode}")]
        HttpRequest req, string materialCode)
    {

        


        return new OkObjectResult("OK");
    }
}

Can someone who has experienced the same issue help me? I've tried everything and I'm still unable to resolve it.

0

There are 0 best solutions below