Erreur d'injection de dépendance ASP.NET Core: impossible de résoudre le service pour le type lors de la tentative d'activation

222

J'ai créé une application .NET Core MVC et j'utilise Dependency Injection and Repository Pattern pour injecter un référentiel dans mon contrôleur. Cependant, j'obtiens une erreur:

InvalidOperationException: impossible de résoudre le service pour le type «WebApplication1.Data.BloggerRepository» lors de la tentative d'activation de «WebApplication1.Controllers.BlogController».

Modèle (Blog.cs)

namespace WebApplication1.Models
{
    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }
    }
}

DbContext (BloggingContext.cs)

using Microsoft.EntityFrameworkCore;
using WebApplication1.Models;

namespace WebApplication1.Data
{
    public class BloggingContext : DbContext
    {
        public BloggingContext(DbContextOptions<BloggingContext> options)
            : base(options)
        { }
        public DbSet<Blog> Blogs { get; set; }
    }
}

Référentiel (IBloggerRepository.cs & BloggerRepository.cs)

using System;
using System.Collections.Generic;
using WebApplication1.Models;

namespace WebApplication1.Data
{
    internal interface IBloggerRepository : IDisposable
    {
        IEnumerable<Blog> GetBlogs();

        void InsertBlog(Blog blog);

        void Save();
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using WebApplication1.Models;

namespace WebApplication1.Data
{
    public class BloggerRepository : IBloggerRepository
    {
        private readonly BloggingContext _context;

        public BloggerRepository(BloggingContext context)
        {
            _context = context;
        }

        public IEnumerable<Blog> GetBlogs()
        {
            return _context.Blogs.ToList();
        }

        public void InsertBlog(Blog blog)
        {
            _context.Blogs.Add(blog);
        }

        public void Save()
        {
            _context.SaveChanges();
        }

        private bool _disposed;

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _context.Dispose();
                }
            }
            _disposed = true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
}

Startup.cs (code pertinent)

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddDbContext<BloggingContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddScoped<IBloggerRepository, BloggerRepository>();

    services.AddMvc();

    // Add application services.
    services.AddTransient<IEmailSender, AuthMessageSender>();
    services.AddTransient<ISmsSender, AuthMessageSender>();
}

Contrôleur (BlogController.cs)

using System.Linq;
using Microsoft.AspNetCore.Mvc;
using WebApplication1.Data;
using WebApplication1.Models;

namespace WebApplication1.Controllers
{
    public class BlogController : Controller
    {
        private readonly IBloggerRepository _repository;

        public BlogController(BloggerRepository repository)
        {
            _repository = repository;
        }

        public IActionResult Index()
        {
            return View(_repository.GetBlogs().ToList());
        }

        public IActionResult Create()
        {
            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Create(Blog blog)
        {
            if (ModelState.IsValid)
            {
                _repository.InsertBlog(blog);
                _repository.Save();
                return RedirectToAction("Index");
            }
            return View(blog);
        }
    }
}

Je ne suis pas sûr de ce que je fais de mal. Des idées?

Kimbaudi
la source
Je sais que c'est une vieille question, mais ... Vous ne devriez pas disposer le contexte db dans un service. Le contexte db est automatiquement supprimé par le résolveur de portée. Si vous le supprimez dans un service, il peut être supprimé lors de l'appel d'un service suivant dans la même demande / portée.
Silvermind
1
Assurez-vous que le service (classe manquante) est ajouté en utilisant ´services.AddTransient <YourClassOrInterface> (); ´
Mauricio Gracia Gutierrez

Réponses:

334

L'exception indique qu'il ne peut pas résoudre le service WebApplication1.Data.BloggerRepositorycar le constructeur de votre contrôleur demande la classe concrète au lieu de l'interface. Alors changez simplement cela:

public BlogController(IBloggerRepository repository)
//                    ^
//                    Add this!
{
    _repository = repository;
}
DavidG
la source
9
Incroyable comme il est facile d'oublier un seul personnage ... merci!
jleach
Quel champion, a reçu cela en utilisant la HttpContextAccessorclasse, il s'avère que j'avais besoin duIHttpContextAccessor
mtbennett
Tellement irrité parce que j'ai perdu plus de 30 minutes à ce sujet. Le pire VS sur Mac vous donne l'erreur "ne pas quitter de manière inattendue". Doit fonctionner sur le terminal pour obtenir l'erreur correcte, puis je suis tombé sur cette solution.
NoloMokgosi
68

J'ai rencontré ce problème car dans la configuration d'injection de dépendance, il me manquait une dépendance d'un référentiel qui est une dépendance d'un contrôleur:

services.AddScoped<IDependencyOne, DependencyOne>();    <-- I was missing this line!
services.AddScoped<IDependencyTwoThatIsDependentOnDependencyOne, DependencyTwoThatIsDependentOnDependencyOne>();
hsop
la source
2
Résolu pour moi, c'était mon problème
anisanwesley
J'ai résolu mon problème, car j'ai reconnu que mes services n'étaient pas dans le bon «espace de noms».
user2982195 le
C'était aussi mon problème. Merci!
Jedidiah le
26

Dans mon cas, j'essayais de faire une injection de dépendance pour un objet nécessitant des arguments de constructeur. Dans ce cas, au démarrage, je viens de fournir les arguments du fichier de configuration, par exemple:

var config = Configuration.GetSection("subservice").Get<SubServiceConfig>();
services.AddScoped<ISubService>(provider => new SubService(config.value1, config.value2));
riqitang
la source
20

J'avais un problème différent, et oui, le constructeur paramétré pour mon contrôleur a déjà été ajouté avec la bonne interface. Ce que j'ai fait était quelque chose de simple. Je vais juste à mon startup.csdossier, où je pourrais voir un appel à la méthode d'enregistrement.

public void ConfigureServices(IServiceCollection services)
{
   services.Register();
}

Dans mon cas, cette Registerméthode était dans une classe distincte Injector. J'ai donc dû y ajouter mes interfaces nouvellement introduites.

public static class Injector
{
    public static void Register(this IServiceCollection services)
    {
        services.AddTransient<IUserService, UserService>();
        services.AddTransient<IUserDataService, UserDataService>();
    }
}

Si vous voyez, le paramètre de cette fonction est this IServiceCollection

J'espère que cela t'aides.

Sibeesh Venu
la source
C'est celui que j'ai oublié d'ajouter. J'ai manqué la référence de l'injecteur au service. Nécessaire à .AddTransient <> (); Merci les gars!
Omzig le
14

Seulement si quelqu'un a la même situation que moi, je fais un tutoriel sur EntityFramework avec la base de données existante, mais lorsque le nouveau contexte de base de données est créé sur les dossiers de modèles, nous devons mettre à jour le contexte au démarrage, mais pas seulement dans les services. AddDbContext mais AddIdentity aussi si vous avez l'authentification des utilisateurs

services.AddDbContext<NewDBContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<NewDBContext>()
                .AddDefaultTokenProviders();
Adrian
la source
10
Public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IEventRepository, EventRepository>();           
}

Vous avez oublié d'ajouter "services.AddScoped" dans la ConfigureServicesméthode de démarrage .

Prakash CS
la source
8

Vous devez ajouter un nouveau service pour DBcontextau démarrage

Défaut

services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection")));

Ajoute ça

services.AddDbContext<NewDBContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("NewConnection")));
Warit Taveekarn
la source
6

J'ai dû ajouter cette ligne dans les ConfigureServices pour pouvoir travailler.

services.AddSingleton<IOrderService, OrderService>();
Mike
la source
5

J'ai eu ce problème à cause d'une erreur plutôt stupide. J'avais oublié de raccorder ma procédure de configuration de service pour découvrir automatiquement les contrôleurs dans l'application ASP.NET Core.

L'ajout de cette méthode l'a résolu:

// Add framework services.
            services.AddMvc()
                    .AddControllersAsServices();      // <---- Super important
feu de soie
la source
4

Dans mon cas, l'API .Net Core 3.0 dans Startup.cs, dans la méthode

public void ConfigureServices(IServiceCollection services)

Je devais ajouter

services.AddScoped<IStateService, StateService>();
Jitendra Sawant
la source
1
Salut, gars! ça l'a fait pour moi. Je savais que dans mon cas, c'était cette solution.
theITvideos
3

J'étais en dessous de l'exception

        System.InvalidOperationException: Unable to resolve service for type 'System.Func`1[IBlogContext]' 
        while attempting to activate 'BlogContextFactory'.\r\n at 
        Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type serviceType, Type implementationType, ISet`1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)\r\n at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(Type serviceType, Type implementationType, ISet`1 callSiteChain)\r\n at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(ServiceDescriptor descriptor, Type serviceType, ISet`1 callSiteChain)\r\n at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(Type serviceType, ISet`1 callSiteChain)\r\n at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateCallSite(Type serviceType, ISet`1 callSiteChain)\r\n at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type serviceType, Type implementationType, ISet`1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)\r\n at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(Type serviceType, Type implementationType, ISet`1 callSiteChain)\r\n at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(ServiceDescriptor descriptor, Type serviceType, ISet`1 callSiteChain)\r\n at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(Type serviceType, ISet`1 callSiteChain)\r\n at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateCallSite(Type serviceType, ISet`1 callSiteChain)\r\n at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(Type serviceType, ServiceProvider serviceProvider)\r\n at System.Collections.Concurrent.ConcurrentDictionaryExtensions.GetOrAdd[TKey, TValue, TArg] (ConcurrentDictionary`2 dictionary, TKey key, Func`3 valueFactory, TArg arg)\r\n at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)\r\n at Microsoft.Extensions.Internal.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)\r\n at lambda_method(Closure , IServiceProvider , Object[] )\r\n at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.<CreateControllerFactory>g__CreateController|0(ControllerContext controllerContext)\r\n at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)\r\n at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()\r\n at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextExceptionFilterAsync()

Parce que je voulais enregistrer Factory pour créer des instances de la classe DbContext Derived IBlogContextFactory et utiliser la méthode Create pour instancier une instance de Blog Context afin que je puisse utiliser le modèle ci-dessous avec l'injection de dépendances et que je puisse également utiliser la simulation pour les tests unitaires.

le modèle que je voulais utiliser est

public async Task<List<Blog>> GetBlogsAsync()
        {
            using (var context = new BloggingContext())
            {
                return await context.Blogs.ToListAsync();
            }
        }

Mais au lieu du nouveau BloggingContext (), je veux injecter l'usine via le constructeur comme ci-dessous la classe BlogController

    [Route("blogs/api/v1")]

public class BlogController : ControllerBase
{
    IBloggingContextFactory _bloggingContextFactory;

    public BlogController(IBloggingContextFactory bloggingContextFactory)
    {
        _bloggingContextFactory = bloggingContextFactory;
    }

    [HttpGet("blog/{id}")]
    public async Task<Blog> Get(int id)
    {
        //validation goes here 
        Blog blog = null;
        // Instantiage context only if needed and dispose immediately
        using (IBloggingContext context = _bloggingContextFactory.CreateContext())
        {
            blog = await context.Blogs.FindAsync(id);
        }
        //Do further processing without need of context.
        return blog;
    }
}

voici mon code d'enregistrement de service

            services
            .AddDbContext<BloggingContext>()
            .AddTransient<IBloggingContext, BloggingContext>()
            .AddTransient<IBloggingContextFactory, BloggingContextFactory>();

et ci-dessous mes modèles et classes d'usine

    public interface IBloggingContext : IDisposable
{
    DbSet<Blog> Blogs { get; set; }
    DbSet<Post> Posts { get; set; }
}

public class BloggingContext : DbContext, IBloggingContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseInMemoryDatabase("blogging.db");
        //optionsBuilder.UseSqlite("Data Source=blogging.db");
    }
}

public interface IBloggingContextFactory
{
    IBloggingContext CreateContext();
}

public class BloggingContextFactory : IBloggingContextFactory
{
    private Func<IBloggingContext> _contextCreator;
    public BloggingContextFactory(Func<IBloggingContext> contextCreator)// This is fine with .net and unity, this is treated as factory function, but creating problem in .netcore service provider
    {
        _contextCreator = contextCreator;
    }

    public IBloggingContext CreateContext()
    {
        return _contextCreator();
    }
}

public class Blog
{
    public Blog()
    {
        CreatedAt = DateTime.Now;
    }

    public Blog(int id, string url, string deletedBy) : this()
    {
        BlogId = id;
        Url = url;
        DeletedBy = deletedBy;
        if (!string.IsNullOrWhiteSpace(deletedBy))
        {
            DeletedAt = DateTime.Now;
        }
    }
    public int BlogId { get; set; }
    public string Url { get; set; }
    public DateTime CreatedAt { get; set; }
    public DateTime? DeletedAt { get; set; }
    public string DeletedBy { get; set; }
    public ICollection<Post> Posts { get; set; }

    public override string ToString()
    {
        return $"id:{BlogId} , Url:{Url} , CreatedAt : {CreatedAt}, DeletedBy : {DeletedBy}, DeletedAt: {DeletedAt}";
    }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

----- Pour résoudre ce problème dans le projet .net Core MVC - J'ai fait ci-dessous les modifications sur l'enregistrement des dépendances

            services
            .AddDbContext<BloggingContext>()
            .AddTransient<IBloggingContext, BloggingContext>()
            .AddTransient<IBloggingContextFactory, BloggingContextFactory>(
                    sp => new BloggingContextFactory( () => sp.GetService<IBloggingContext>())
                );

En bref, dans .net, le développeur principal est responsable d'injecter la fonction d'usine, ce qui dans le cas de Unity et .Net Framework a été pris en charge.

Rajnikant
la source
3

Ce problème est dû au fait que vous n'avez pas enregistré le composant d'accès aux données avec l'interface écrite pour lui. Essayez d'utiliser comme suit

services.AddTransient<IMyDataProvider, MyDataAccess>();`
Shailesh Tiwari
la source
2

Si vous utilisez AutoFac et obtenez cette erreur, vous devez ajouter une instruction "As" pour spécifier le service que l'implémentation concrète implémente.

C'est à dire. vous devriez écrire:

containerBuilder.RegisterType<DataService>().As<DataService>();

au lieu de

containerBuilder.RegisterType<DataService>();
thebfactor
la source
2

ohh, merci @kimbaudi, j'ai suivi ce tuts

https://dotnettutorials.net/lesson/generic-repository-pattern-csharp-mvc/

et a eu la même erreur que votre. Mais après avoir lu votre code, j'ai découvert que ma solution ajoutait

services.AddScoped (IGenericRepository, GenericRepository);

dans la méthode ConfigureServices dans le fichier StartUp.cs =))

Fils
la source
2

J'ai eu le même problème et j'ai découvert que mon code utilisait l'injection avant son initialisation.

services.AddControllers(); // Will cause a problem if you use your IBloggerRepository in there since it's defined after this line.
services.AddScoped<IBloggerRepository, BloggerRepository>();

Je sais que cela n'a rien à voir avec la question, mais depuis que j'ai été envoyé sur cette page, je pense que cela peut être utile à quelqu'un d'autre.

Sauleil
la source
2

La résolution d'un service est effectuée avant même que le code de classe ne soit atteint, nous devons donc vérifier nos injections de dépendances.

Dans mon cas j'ai ajouté

        services.AddScoped<IMeasurementService, MeasurementService>();

dans StartupExtensions.cs

Onat Korucu
la source
1

Ajouter des services.AddSingleton (); dans votre méthode ConfigureServices du fichier Startup.cs de votre projet.

public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
        // To register interface with its concrite type
        services.AddSingleton<IEmployee, EmployeesMockup>();
    }

Pour plus de détails, veuillez visiter cette URL: https://www.youtube.com/watch?v=aMjiiWtfj2M

pour toutes les méthodes (c'est-à-dire AddSingleton vs AddScoped vs AddTransient) Veuillez visiter cette URL: https://www.youtube.com/watch?v=v6Nr7Zman_Y&list=PL6n9fhu94yhVkdrusLaQsfERmL_Jh4XmU&index=44 )

Bhanu Pratap
la source
0

j'ai remplacé

services.Add(new ServiceDescriptor(typeof(IMyLogger), typeof(MyLogger)));

Avec

services.AddTransient<IMyLogger, MyLogger>();

Et cela a fonctionné pour moi.

satish madarapu
la source
0

Changer BloggerRepository en IBloggerRepository

Shah Zaiƞ
la source
0

J'ai eu des problèmes en essayant d'injecter à partir de mon fichier Program.cs , en utilisant CreateDefaultBuilder comme ci-dessous, mais j'ai fini par le résoudre en ignorant le classeur par défaut. (voir ci-dessous).

var host = Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
    webBuilder.ConfigureServices(servicesCollection => { servicesCollection.AddSingleton<ITest>(x => new Test()); });
    webBuilder.UseStartup<Startup>();
}).Build();

Il semble que la construction aurait dû être effectuée à l'intérieur de ConfigureWebHostDefaults pour que cela fonctionne, sinon la configuration sera ignorée, mais corrigez-moi si je me trompe.

Cette approche a bien fonctionné:

var host = new WebHostBuilder()
.ConfigureServices(servicesCollection =>
{
    var serviceProvider = servicesCollection.BuildServiceProvider();
    IConfiguration configuration = (IConfiguration)serviceProvider.GetService(typeof(IConfiguration));
    servicesCollection.AddSingleton<ISendEmailHandler>(new SendEmailHandler(configuration));
})
.UseStartup<Startup>()
.Build();

Cela montre également comment injecter une dépendance déjà prédéfinie dans .net core ( IConfiguration ) à partir de

Sgedda
la source
-1

J'ai eu cette erreur parce que j'ai déclaré une variable (au-dessus de la méthode ConfigureServices) de type qui était mon contexte. J'avais:

CupcakeContext _ctx

Je ne sais pas à quoi je pensais. Je sais que c'est légal de faire cela si vous passez un paramètre à la méthode Configure.

Athomas
la source
-1

J'ai reçu l'erreur: "impossible de résoudre la dépendance xxxxxxxx pour toutes les versions de .net core". J'ai essayé tout ce qui est disponible sur Internet et je suis resté coincé pendant des jours. La seule solution que j'ai trouvée était d'ajouter le fichier nuget.config dans le projet, puis d'utiliser la restauration dotnet pour le faire fonctionner.

Contenu du fichier nuget.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="AspNetCore" value="https://dotnet.myget.org/F/aspnetcore-ci-dev/api/v3/index.json" />
    <add key="AspNetCoreTools" value="https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json" />
    <add key="NuGet" value="https://api.nuget.org/v3/index.json" />
  </packageSources>
</configuration>
Shantanu Gautam
la source