Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Some services are not able to be constructed

Tags:

I have a ASP.NET Core 3.1 MVC solution, where I have created a class for collecting common used functions in the solution.

I have registered the class in startup.cs like this:

services.AddSingleton<ITeamFunctions, TeamFunctions>();

Interface (ITeamFunctions) for the class looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace PI.Interfaces
{
    public interface ITeamFunctions
    {
        Task UpdateTeamStat(int teamId);
        Task<float> CalculatePriceToPay();
    }
}

And the class itself like this:

using System.Linq;
using System.Security.Claims;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Transactions;

namespace PI.Utility
{
    public class TeamFunctions : ITeamFunctions
    {
        private readonly ApplicationDbContext _db;
        private readonly UserManager<IdentityUser> _userManager;
        private readonly IHttpContextAccessor _httpContextAccessor;

        public TeamFunctions(ApplicationDbContext db, UserManager<IdentityUser> userManager, IHttpContextAccessor httpContextAccessor)
        {
            _db = db;
            _userManager = userManager;
            _httpContextAccessor = httpContextAccessor;
        }

        Task UpdateTeamStat(int teamId)
        {
        //code here
        }

        Task<float> CalculatePriceToPay();
        {
        //code here
        }



When running the solution it gives me this error:

System.AggregateException: 'Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: PI.Interfaces.ITeamFunctions Lifetime: Singleton ImplementationType: PI.Utility.TeamFunctions': Unable to resolve service for type 'Microsoft.AspNetCore.Identity.UserManager`1[Microsoft.AspNetCore.Identity.IdentityUser]' while attempting to activate 'PI.Utility.TeamFunctions'.)'

Disabling IdentityUser injection in the TeamFunction, and it complains about my ApplicationDbContext. I have experimented with scope in startup.cs (changing AddSingleton with AddScoped) but without any difference. I have a similar architecture in a Core 2.2 solution where it works.

Wonder what I'm missing?

EDIT: The complete startup.cs looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.EntityFrameworkCore;
using PI.Data;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using PI.Models;
using Microsoft.AspNetCore.Http;
using PI.Utility;
using PI.Interfaces;
using Microsoft.AspNetCore.Identity.UI.Services;
using PI.Service;
using PI.Installers;

namespace PI
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection")));

            services.AddDefaultIdentity<ApplicationUser>()
                .AddRoles<IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>();

            services.Configure<IdentityOptions>(options =>                  
            {
                // Default Password settings.
                options.Password.RequireDigit = false;
                options.Password.RequireLowercase = true;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireUppercase = false;
                options.Password.RequiredLength = 6;
                options.Password.RequiredUniqueChars = 0;
            });

            services.Configure<CookiePolicyOptions>(options =>
            {
                options.CheckConsentNeeded = context => false;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddSession(options =>                                  
            {
                options.Cookie.IsEssential = true;
                options.IdleTimeout = TimeSpan.FromMinutes(60);
                options.Cookie.HttpOnly = true;
            });

            services.AddSingleton<IEmailSender, EmailSender>();
            services.Configure<EmailOptions>(Configuration);
            services.AddSingleton<ITeamFunctions, TeamFunctions>();
            services.AddControllersWithViews();
            services.AddRazorPages().AddRazorRuntimeCompilation();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();
            app.UseSession();                                              

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapAreaControllerRoute(
                    name: "admin",
                    areaName: "admin",
                    pattern: "Admin/{controller=User}/{action=Index}/{id?}"
                    );

                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{area=Main}/{controller=Home}/{action=Index}/{id?}");
                endpoints.MapRazorPages();
            });

            app.UseCookiePolicy(); 
        }
    }
}
like image 984
René Wester Avatar asked Jun 14 '20 16:06

René Wester


1 Answers

In the shown ConfigureServices

services.AddDefaultIdentity<ApplicationUser>()
//...

internally registers UserManager<ApplicationUser>, but the constructor of the class to be resolved (TeamFunctions) depends on UserManager<IdentityUser>.

The container therefore does not know how to handle that dependency based on the shown configuration services in startup and throws the stated exception.

Update the constructor to explicitly expect the type that was configured.

//ctor
public TeamFunctions(
    ApplicationDbContext db, 
    UserManager<ApplicationUser> userManager, //<-- note the type used.
    IHttpContextAccessor httpContextAccessor) {

    //...omitted for brevity

}

Secondly, UserManager is added with scoped service lifetime

//...

services.TryAddScoped<UserManager<TUser>>();

//...

Source code

And since Scoped lifetime does not play well with Singleton, then the service should also be added as scoped.

//...

services.AddScoped<ITeamFunctions, TeamFunctions>();

//...
like image 61
Nkosi Avatar answered Oct 02 '22 15:10

Nkosi