web-dev-qa-db-fra.com

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

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, je reçois une erreur:

InvalidOperationException: Impossible de résoudre le service pour le type 'WebApplication1.Data.BloggerRepository' lors de l'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 mal. Des idées?

71
kimbaudi

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

public BlogController(IBloggerRepository repository)
//                    ^
//                    Add this!
{
    _repository = repository;
}
128
DavidG

Dans mon cas, j'essayais de faire une injection de dépendance pour un objet qui nécessitait des arguments de constructeur. Dans ce cas, lors du 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));
13
riqitang

Je fais un tutoriel d'EntityFramework avec une base de données existante, mais lorsque le nouveau contexte de base de données est créé sur les dossiers des modèles, nous devons le mettre à jour au démarrage, mais pas uniquement 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();
10
Adrian

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 viens d'aller dans mon fichier startup.cs, où je pouvais voir un appel à la méthode d'inscription.

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

Dans mon cas, cette méthode Register était dans une classe séparée 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 pour cette fonction est this IServiceCollection

J'espère que cela t'aides.

4
Sibeesh Venu

J'ai rencontré ce problème car dans la configuration de l'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>();
4
hsop

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 les contrôleurs automatiquement dans l'application ASP.NET Core.

L'ajout de cette méthode a résolu le problème:

// Add framework services.
            services.AddMvc()
                    .AddControllersAsServices();      // <---- Super important
2
silkfire

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

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

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

services.AddScoped (IGenericRepository, GenericRepository);

into ConfigureServices dans le fichier StartUp.cs =))

2
Son

Vous devez ajouter un nouveau service pour DBcontext au démarrage 

Default

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

Ajoute ça

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

Je devais ajouter cette ligne dans ConfigureServices pour pouvoir fonctionner.

services.AddSingleton<IOrderService, OrderService>();
2
Mike
Public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IEventRepository, EventRepository>();           
}

Vous avez oublié d'ajouter Addscope à la méthode de démarrage Configureservices.

1
Prakash CS

Si vous utilisez AutoFac et obtenez cette erreur, vous devez ajouter une instruction "En tant que" pour spécifier le service implémenté par la mise en oeuvre concrète.

C'est à dire. vous devriez écrire:

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

au lieu de

containerBuilder.RegisterType<DataService>();
1
thebfactor

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 celui-ci. Essayez d'utiliser comme suit

services.AddTransient<IMyDataProvider, MyDataAccess>();`
0
Shailesh Tiwari

J'ai remplacé services.Add(new ServiceDescriptor(typeof(IMyLogger), typeof(MyLogger))) par services.AddTransient<IMyLogger, MyLogger>()

Et cela a fonctionné pour moi.

0
satish madarapu

Je devenais 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 que register Factory crée des instances de la classe IBlogContextFactory de DbContext Derived et utilise la méthode Create pour instancier une instance de Blog Context afin que je puisse utiliser le modèle ci-dessous avec Injection de dépendance et également utiliser le moquage pour le test unitaire.

le motif que je voulais utiliser est 

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

Mais au lieu de nouveau BloggingContext (), je veux Injecter la fabrique via le constructeur comme dans la classe BlogController ci-dessous

    [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 sont 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 effectué les modifications ci-dessous lors de l'enregistrement de la dépendance

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

En bref, le développeur principal .net est responsable de l’injection de la fonction d’usine, qui a été prise en charge sous Unity et .Net Framework.

0
RAJ

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'ai eu:

CupcakeContext _ctx

Pas sûr de ce que je pensais. Je sais que c'est légal de le faire si vous passez un paramètre à la méthode Configure.

0
athomas