Błąd wstrzykiwania zależności platformy ASP.NET Core: Nie można rozpoznać usługi dla typu podczas próby aktywacji

198

Utworzyłem aplikację .NET Core MVC i do wstrzykiwania repozytorium do mojego kontrolera używam Dependency Injection and Repository Pattern. Jednak pojawia się błąd:

InvalidOperationException: Nie można rozwiązać usługi typu „WebApplication1.Data.BloggerRepository” podczas próby aktywacji „WebApplication1.Controllers.BlogController”.

Model (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; }
    }
}

Repozytorium (IBloggerRepository.cs i 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 (odpowiedni kod)

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>();
}

Kontroler (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);
        }
    }
}

Nie jestem pewien, co robię źle. Jakieś pomysły?

kimbaudi
źródło
Wiem, że to stare pytanie, ale ... Nie należy wyrzucać kontekstu db wewnątrz usługi. Kontekst db jest automatycznie usuwany przez resolver zakresu. Jeśli usuniesz go w ramach usługi, może zostać usunięty podczas wywoływania kolejnej usługi w ramach tego samego żądania / zakresu.
Silvermind
1
Upewnij się, że usługa (brakująca klasa) została dodana przy użyciu ´services.AddTransient <YourClassOrInterface> (); ´
Mauricio Gracia Gutierrez

Odpowiedzi:

292

Wyjątek mówi, że nie może rozwiązać usługi, WebApplication1.Data.BloggerRepositoryponieważ konstruktor w kontrolerze prosi o konkretną klasę zamiast interfejsu. Po prostu zmień to:

public BlogController(IBloggerRepository repository)
//                    ^
//                    Add this!
{
    _repository = repository;
}
DavidG
źródło
6
Niesamowite, jak łatwo przeoczyć jedną postać ... dzięki!
jleach
Co za mistrz, który otrzymał to podczas korzystania z HttpContextAccessorklasy, okazało się, że potrzebowałemIHttpContextAccessor
mtbennett
Tak zirytowany, ponieważ odłożyłem na to ponad 30 minut. Najgorszy VS na Macu daje błąd „nie wychodź niespodziewanie”. Musi działać na terminalu, aby uzyskać poprawny błąd, a potem wpadłem na to rozwiązanie.
NoloMokgosi
57

Natknąłem się na ten problem, ponieważ w konfiguracji wstrzykiwania zależności brakowało zależności repozytorium, która jest zależnością kontrolera:

services.AddScoped<IDependencyOne, DependencyOne>();    <-- I was missing this line!
services.AddScoped<IDependencyTwoThatIsDependentOnDependencyOne, DependencyTwoThatIsDependentOnDependencyOne>();
hsop
źródło
Dla mnie rozwiązany, to był mój problem
anisanwesley,
Rozwiązałem mój problem, ponieważ rozpoznałem, że moje usługi nie znajdują się we właściwej „przestrzeni nazw”.
user2982195
24

W moim przypadku próbowałem wykonać wstrzyknięcie zależności dla obiektu, który wymagał argumentów konstruktora. W tym przypadku podczas uruchamiania podałem tylko argumenty z pliku konfiguracyjnego, na przykład:

var config = Configuration.GetSection("subservice").Get<SubServiceConfig>();
services.AddScoped<ISubService>(provider => new SubService(config.value1, config.value2));
riqitang
źródło
18

Miałem inny problem i tak, sparametryzowany konstruktor mojego kontrolera został już dodany z odpowiednim interfejsem. To, co zrobiłem, było proste. Po prostu idę do mojegostartup.cs pliku, w którym mogę zobaczyć metodę rejestracji.

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

W moim przypadku ta Registermetoda była w osobnej klasie Injector. Musiałem więc tam dodać moje nowo wprowadzone interfejsy.

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

Jeśli widzisz, parametrem tej funkcji jest this IServiceCollection

Mam nadzieję że to pomoże.

Sibeesh Venu
źródło
To jest ten, o którym zapomniałem dodać. Brakowało mi odniesienia do wtryskiwacza do usługi. Potrzebny do .AddTransient <> (); Dzięki chłopaki!
Omzig
14

Tylko jeśli ktoś ma taką samą sytuację jak ja, robię tutorial EntityFramework z istniejącą bazą danych, ale kiedy nowy kontekst bazy danych jest tworzony w folderach modeli, musimy zaktualizować kontekst podczas uruchamiania, ale nie tylko w usługach. AddDbContext, ale także AddIdentity, jeśli masz uwierzytelnianie użytkowników

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

services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<NewDBContext>()
                .AddDefaultTokenProviders();
Adrian
źródło
7

Musisz dodać nową usługę dla DBcontext podczas uruchamiania

Domyślna

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

Dodaj

services.AddDbContext<NewDBContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("NewConnection")));
Warit Taveekarn
źródło
7
Public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IEventRepository, EventRepository>();           
}

Zapomniałeś dodać „services.AddScoped” w ConfigureServicesmetodzie uruchamiania .

Prakash CS
źródło
5

Dostałem ten problem z powodu dość głupiej pomyłki. Zapomniałem zawiesić procedury konfiguracji usługi, aby automatycznie wykrywać kontrolery w aplikacji ASP.NET Core.

Dodanie tej metody rozwiązało to:

// Add framework services.
            services.AddMvc()
                    .AddControllersAsServices();      // <---- Super important
jedwabny ogień
źródło
5

Musiałem dodać tę linię do ConfigureServices, aby działać.

services.AddSingleton<IOrderService, OrderService>();
Mikrofon
źródło
3

Byłem poniżej wyjątku

        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()

Ponieważ chciałem zarejestrować Factory, aby utworzyć instancje klasy pochodnej DbContext IBlogContextFactory i użyć metody Create, aby utworzyć instancję instancji Blog Context, dzięki czemu będę mógł korzystać z poniższego wzorca wraz z zależnością Wstrzykiwanie, a także używać kpiny do testowania jednostkowego.

wzór, którego chciałem użyć, to

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

Ale zamiast nowego BloggingContext () chcę wstrzyknąć fabrykę za pomocą konstruktora, jak w poniższej klasie 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;
    }
}

oto mój kod rejestracyjny usługi

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

a poniżej są moje modele i klasy fabryczne

    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; }
}

----- Aby to naprawić w projekcie .NET Core MVC - Wprowadziłem poniższe zmiany dotyczące rejestracji zależności

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

W skrócie, deweloper rdzenia .net jest odpowiedzialny za wstrzyknięcie funkcji fabryki, co w przypadku Unity i .Net Framework zostało załatwione.

Rajnikant
źródło
3

Ten problem jest spowodowany tym, że komponent dostępu do danych nie został zarejestrowany w interfejsie dla niego napisanym. Spróbuj użyć w następujący sposób

services.AddTransient<IMyDataProvider, MyDataAccess>();`
Shailesh Tiwari
źródło
2

Jeśli używasz funkcji AutoFac i pojawia się ten błąd, należy dodać instrukcję „As”, aby określić usługę, którą implementuje konkretna implementacja.

To znaczy. powinieneś napisać:

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

zamiast

containerBuilder.RegisterType<DataService>();
czynnik
źródło
2

Rozwiązanie usługi odbywa się nawet przed osiągnięciem kodu klasy, dlatego musimy sprawdzić nasze zastrzyki zależności.

W moim przypadku dodałem

        services.AddScoped<IMeasurementService, MeasurementService>();

w StartupExtensions.cs

Onat Korucu
źródło
1

Dodaj services.AddSingleton (); w metodzie ConfigureServices pliku Startup.cs twojego projektu.

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

Aby uzyskać więcej informacji, odwiedź ten adres URL: https://www.youtube.com/watch?v=aMjiiWtfj2M

dla wszystkich metod (tj. AddSingleton vs AddScoped vs AddTransient) Odwiedź ten adres URL: https://www.youtube.com/watch?v=v6Nr7Zman_Y&list=PL6n9fhu94yhVkdrusLaQsfERmL_Jh4XmU&index=44 )

Bhanu Pratap
źródło
1

Miałem ten sam problem i dowiedziałem się, że mój kod używał zastrzyku przed jego zainicjowaniem.

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

Wiem, że to nie ma nic wspólnego z tym pytaniem, ale ponieważ zostałem wysłany na tę stronę, doszedłem do wniosku, że przyda mi się ktoś inny.

Sauleil
źródło
0

Wymieniłem

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

Z

services.AddTransient<IMyLogger, MyLogger>();

I zadziałało dla mnie.

satish madarapu
źródło
-1

Wystąpił ten błąd, ponieważ zadeklarowałem zmienną (powyżej metody ConfigureServices) typu, która była moim kontekstem. Miałem:

CupcakeContext _ctx

Nie jestem pewien, co myślałem. Wiem, że jest to legalne, jeśli przekazujesz parametr do metody Konfiguruj.

atomy
źródło
-1

Wystąpił błąd: „nie można rozwiązać zależności xxxxxxxx dla wszystkich wersji rdzenia .net”. Próbowałem wszystkiego dostępnego w Internecie i utknąłem na kilka dni. Jedynym rozwiązaniem, jakie wymyśliłem, było dodanie pliku nuget.config do projektu, a następnie użycie przywracania dotnet, aby działało.

Zawartość pliku 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
źródło