I am building a REST API as an Azure Functions application that leverages Dependency Injection. The other day, when I deployed the application once again to Azure, most of the endpoints failed to execute due to a very weird error. The application was running fine when running it locally. The error description was.

Registered factory delegate returns service is not assignable to ambiently scoped container with {no name, Parent={no name, Parent={no name, Parent={no name}}}}.

A highly compacted version of the stack trace looked like this.

DryIoc.ContainerException:
   at DryIoc.Throw.ThrowIfNotInstanceOf
   at DryIoc.Scope.GetOrAdd
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService
   at Microsoft.Azure.WebJobs.Host.Executors.FunctionInvoker`2.CreateInstance
   at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor+ParameterHelper.Initialize

This article is me trying to understand why this happened only in Azure and not locally, and how I finally fixed it. I also created a repository on GitHub with source code that you can use to reproduce the problem. There are two tags: before-fix and after-fix that represent different versions of the source code. The code under the before-fix will fail to work when deployed to Azure.

For additional reading about Azure Functions, have a look at my other Azure Functions articles. If you want to learn more about how to build APIs with Azure Functions, you could for instance start with this learning module from Microsoft.

Finding the Source of the Problem

From the stack trace above I realized that the problem had something to do with Dependency Injection and how I introduced my services. What was quite confusing was that it all worked locally, but failed when deployed to Azure.

I have designed the application with a set of function classes that implement the HTTP triggered functions in the application. These function classes depend on service implementations that in turn depend on option classes that the application populates from the application settings in the Startup class.

There were quite a few service implementations and option classes, and I couldn’t tell right away which of them was the problem. I have several function classes with various HTTP triggered functions. How I distribute the function across multiple function classes is mainly based on what dependencies the functions have. So, quite soon I noticed that all functions that needed my data service class (the class that I used to access the database with) were all functioning. So, it could not have been a problem with my data service or its configuration settings.

The rest of the services were a little bit trickier. So I created one additional function class with just one HTTP triggered function that I could use to trigger the Dependency Injection framework to create the function class with all its dependencies. I did not use this class or function for anything else than just trigger the Dependency Injection framework in a more controlled way. Then, I added all my service and option classes that I had not yet cleared as dependencies to the function class, one by one. After each change, I deployed the application to Azure, and called the HTTP triggered function.

Examining the Problematic Dependency

I was not that lucky to find the problem with my first attempt. It took a few rounds of changing dependency to my test class and deploying it to Azure. Luckily it did not take that long to do a round.

The rouge dependency was actually an options class that I had successfully used previously. And also successfully deployed the application to Azure. That’s probably why I did not suspect it at first.

I am a strongly typed kind of guy. That’s why I also like to model my application settings as a hierarchy of classes. Then deserialize the settings to a hierarchy of objects. That was the case with this application too. So, I basically had a “root config” class with child properties representing child configuration sections.

This root class was designed similar to the code shown below.

public class AppRootSection
{
    public DatabaseOptions Data { get; set; }
    public ApplicationOptions Application { get; set; }
}

In the Startup class, I used the following code to deserialize the application configuration into an instance of the AppRootSection class, and introduce it as a service, as shown below.

builder.Services
    .AddSingleton(sp =>
    {
        var config = sp.GetRequiredService<IConfiguration>();
        return config.GetSection("myapp").Get<AppRootSection>();
    });

Then, to introduce the DatabaseOptions and ApplicationOptions classes as services, I added the following code.

builder.Services
    .AddSingleton(sp =>
    {
        var root = sp.GetRequiredService<AppRootSection>();
        return root.Application;
    })
    .AddSingleton(sp =>
    {
        var root = sp.GetRequiredService<AppRootSection>();
        return root.Data;
    });

It was probably at this point that something went wrong in the hosting environment when running in Azure. Again, when running locally, this also worked without any problem.

Resolving the Problem

I tried various ways to rewrite my code, but none of them worked. Then, I finally got it working by isolating each option class without a parent class instance. I was not using the AppRootSection configuration class in any service anyway, so I got rid of that, and rewrote my option class creation in Startup as shown below.

builder.Services
    .AddSingleton(sp =>
    {
        var options = new ApplicationOptions();
        sp.GetRequiredService<IConfiguration>()
            .GetSection("myapp:application")
            .Bind(options);
        return options;
    })
    .AddSingleton(sp =>
    {
        var options = new DatabaseOptions();
        sp.GetRequiredService<IConfiguration>()
            .GetSection("myapp:data")
            .Bind(options);
        return options;
    });

A little bit longer perhaps, but working. And, in my application I was working on, I actually created an extension method for the options class. So each option class only required one line of code in Startup. That was all that was required to get things fixed. I did not have to do any changes to the application itself. Just the way I introduced the services to the Dependency Injection framework.

The Root Cause

I don’t exactly know what actually was the root cause to this problem. If I figure it out, I will edit this article to include a description of that. I’d also be happy if you’d leave a comment with your ideas about the root cause. I still decided to write this article, because I know how to fix the problem. And that might help someone else and save them from the countless hours I used to try to figure out what was going on.

However, I suspect that the underlying reason may have something to do with the fact that I had service instances holding object references to other service instances that were created independently from each other. I might be wrong too, but I’ll update this article if I ever figure the root cause out.

Until then, I hope that you did not have to spend as many hours as I did, in case you found this article while trying to figure how to fix the error message below.

Registered factory delegate returns service is not assignable to ambiently scoped container with {no name, Parent={no name, Parent={no name, Parent={no name}}}}.

0 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *