A suitable constructor for type 'RestDataService' could not be located
Asked Answered
G

11

33

While running the .Net Core 2.0 API endpoint getting below error.

A suitable constructor for type 'RestDataService' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.

 public partial class RestDataService : IRestDataService
    {
        private static HttpClient _client;
        private static AppConfiguration _configuration;
        private const short MaxRetryAttempts = 3;
        private const short TimeSpanToWait = 2;

        public RestDataService(AppConfiguration configuration)
        {
            _client = configuration.HttpClient;
            _configuration = configuration;
        }
........

And my startup class is something like this :

  // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {

            var config = new AppConfiguration
            {
                Environment = Configuration["environment"],
            };

            services.AddMvc().AddJsonOptions(o => o.SerializerSettings.NullValueHandling = NullValueHandling.Include);
            services.AddMemoryCache();
            services.AddCors();
            services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
                services.AddSingleton(Configuration);
                services.AddSingleton(config);
            services.AddLogging();
            services.AddTransient<IRestDataService, RestDataService>();

services.AddHttpClient<IRestDataService, RestDataService>()
                .AddPolicyHandler(request => request.Method == HttpMethod.Get ? retryPolicy : noOp);

Any suggestions, to get rid of this? constructor is already public and all the parameters are registered in startup file

Gael answered 11/5, 2019 at 4:48 Comment(1)
Share us the code for AppConfiguration. Why did you configure HttpClient in AppConfiguration?Timeless
F
119

I received the error "A suitable constructor for type '<type>' could not be located." after accidentally generating a protected constructor instead of public constructor. Marking it back to public fixed it.

Fechner answered 22/3, 2021 at 16:36 Comment(12)
Thankyou so much for this. I had exactly the same issue. I just spent the last 30 mins removing constructor parameters and explicitly getting services from the provider. This post solved my issue. It should have been so obvious but i didn't spot it.Trypanosomiasis
And the Oscar goes to... CryptcJujitsu
Also, ensure that Controller constructor is public, not internalFig
Thank you, I lost an hour trying to figure out what was going on... Such a small detail, you helped me a lot!Gothurd
What if the <type> is implemented as Singleton? its constructor is private by design then why must it have a public constructor?Sanitary
@Sanitary It should remain private, with a public Instance property, like: public static Singleton Instance { get { return _instance; } } -- I recommend: singletonsFechner
Yup, this one right I also overlooked it.Galliard
Thank you @Fechner - The issue was kind of obvious but it's good that you pointed us to the right path. Obviously Some services are not able to be constructed if the construction is Protected or Private.Casablanca
A lot of people already thanking you, but I wanted to add mint too. I was having a problem with a different interface, but the solution was the same. I have an instance field, but the builder needed just the type and wasn't able to use the instance field. I got the code from an MS article and it had the constructor as private so only the instance could call it.Ascham
It's always the stupid stuff that drives you crazy. Thanks so much for this.Acidhead
You are our Hero. Couldn't understand what was wrong. Thanks.Lapillus
The same error also happens if your constructor accessibility is internal. Apparently they have to always be public.Novel
T
16

For AddHttpClient, you need to provide HttpClient parameter for RestDataService. And, you need to register AppConfiguration.

  1. RestDataService

    public class RestDataService: IRestDataService
    {
        private static HttpClient _client;
        private static AppConfiguration _configuration;
        private const short MaxRetryAttempts = 3;
        private const short TimeSpanToWait = 2;
    
        public RestDataService(AppConfiguration configuration
            , HttpClient client)
        {
            _client = configuration.HttpClient;
            _configuration = configuration;
        }
    }
    
  2. Startup.cs

    var config = new AppConfiguration
    {
        Environment = Configuration["environment"],
    };
    
    services.AddSingleton(typeof(AppConfiguration), config);
    services.AddHttpClient<IRestDataService, RestDataService>();
    
Timeless answered 13/5, 2019 at 7:18 Comment(3)
I Get it fixed by getting the HttpClient from IHttpClientFactory.GetClient()Gael
It works to add HttpClient as a parameter, but feels wrong that you're not actually using it.Simpleton
This was my mistake. I was trying to use an IHttpClientFactory in the service client class constructor, but I needed to use HttpClient directly instead.Zoology
N
7

I just ran across this issue because I was accidentally registering HttpClient to an interface.

public void ConfigureServices(IServiceCollection services) {
  // !! Wrong, this is done automatically by AddHttpClient<IMyService, MyService>()
  services.AddTransient<IMyService, MyService>();

  // !! There is no suitable constructor for IMyService, since it is an interface.
  services.AddHttpClient<IMyService>();

  // Instead, let AddHttpClient manage your service's lifetime, 
  // and tell it the implementation.
  // It is registered with a Transient lifetime as described below.
  services.AddHttpClient<IMyService, MyService>();
}

Important context, from source: https://www.stevejgordon.co.uk/ihttpclientfactory-patterns-using-typed-clients-from-singleton-services

When defining typed clients in your ConfigureServices method, the typed service is registered with transient scope. This means that a new instance is created by the DI container every time one is needed. The reason this occurs is that a HttpClient instance is injected into the typed client instance. That HttpClient instance is intended to be short lived so that the HttpClientFactory can ensure that the underlying handlers (and connections) are released and recycled.

Nummulite answered 23/6, 2021 at 16:19 Comment(0)
P
4

You have to define which concrete class of interface you want to use for IRestDataService. So, define like this.

services.AddTransient<IRestDataService, RestDataService>();

Remove static keyword before AppConfiguration.

private readonly AppConfiguration _configuration;
Prom answered 11/5, 2019 at 4:57 Comment(4)
It is already there in startup class under ConfigurationServices() method.. But with Transient scope, services.AddTransient<IRestDataService, RestDataService>();Gael
So make sure it is defined in services once. And remove static type from AppConfiguration declaretion in RestDataService.Prom
Any thoughts ? Issue occurs for only specific controller endpoints. Other Controller endpoints working fineGael
Did you remove static type of AppConfiguationProm
O
2

This is such a crazy edge-case answer, but if you are building an executable console app with dependency injection, the build settings could be messing you up.

If you select Trim unused code, the publish might trim stuff that is only being injected and not called directly.

Octad answered 31/3, 2023 at 18:43 Comment(0)
G
2

The problem you write your field private you must write them as public like this:

    public static HttpClient _client;
    public static AppConfiguration _configuration;
    public const short MaxRetryAttempts = 3;
    public const short TimeSpanToWait = 2;
Grime answered 11/5, 2023 at 23:48 Comment(0)
S
1

In my case I got this problem when inattentively duplicating row with DI service registration to register new service but accidentally missed that this is .AddHttpClient() method instead of .AddTransient() or .AddScoped()

Sabra answered 25/4, 2022 at 7:52 Comment(0)
A
1

This doesn't directly answer the question, but is related.

For anyone using ActivatorUtilities.CreateInstance<T>(IServiceProvider, params object[] parameters) it's worth mentioning that every parameter must be in the parameters of the class you are creating. I.e.

public class Example 
{
    public Example(AppConfiguration configuration) 
    {
       ...
    }
}

_ = ActivatorUtilities.CreateInstance<Example>(_serviceProvider, new HttpClient())

This will fail as the Example class doesn't have a constructor that has a HttpClient paramter

Asyut answered 18/5, 2022 at 12:42 Comment(0)
E
1

I had forgotten to add a RequestDelegate next parameter to my middleware constructor. Without this, I would get an exception with the message "A suitable constructor for type [Type] could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor" upon calling IApplicationBuilder.UseMiddleware. My constructor was already public, and my services were all registered. I just needed to add RequestDelegate next to my parameters.

Eta answered 14/10, 2022 at 14:48 Comment(0)
N
0

If you get this error with AddHttpClient, the problem is probably caused by not passing the HttpClient object into the Proxy.

Proxy.cs

private readonly HttpClient _httpClient;

public Proxy(HttpClient httpClient)
{
    _httpClient = httpClient;
}

Startup.cs

services.AddHttpClient<IProxy, Proxy>

When using with AddHttpClient, HttpClient is required in the constructor

Nitrate answered 30/11, 2023 at 8:2 Comment(0)
S
0

If you already exhausted with above answers try reordering the service registrations. I have been seeing this error when I had the HttpClient dependency registration after the EmployeeService registration.

builder.Services.AddSingleton<IEmployeeService, EmployeeService>();

builder.Services.AddHttpClient<IEmployeeService, EmployeeService>("FakeApiStore", o =>
{
    var baseUrl = builder.Configuration.GetSection("ThirdPartyBaseUrl").Value;
    o.BaseAddress = new Uri(baseUrl);
});

Moving the registration of HttpClient before the EmployeeService registration resolved the error. This makes sense because the EmployeeService is dependent on HttpClient and is being passed as a constructor parameter to EmployeeService.

builder.Services.AddHttpClient<IEmployeeService, EmployeeService>("FakeApiStore", o =>
    {
        var baseUrl = builder.Configuration.GetSection("ThirdPartyBaseUrl").Value;
        o.BaseAddress = new Uri(baseUrl);
    });

builder.Services.AddSingleton<IEmployeeService, EmployeeService>();
Sergius answered 17/5 at 10:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.