ข้อผิดพลาดการฉีดของ ASP.NET Core Dependency: ไม่สามารถแก้ไขบริการสำหรับประเภทในขณะที่พยายามเปิดใช้งาน


198

ฉันสร้างแอปพลิเคชั่น. NET Core MVC และใช้ Dependency Injection และ Repository Pattern เพื่อฉีดที่เก็บไปยังคอนโทรลเลอร์ของฉัน อย่างไรก็ตามฉันได้รับข้อผิดพลาด:

InvalidOperationException: ไม่สามารถแก้ไขบริการสำหรับประเภท 'WebApplication1.Data.BloggerRepository' ในขณะที่พยายามเปิดใช้งาน 'WebApplication1.Controllers.BlogController'

แบบจำลอง (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; }
    }
}

Repository (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 (รหัสที่เกี่ยวข้อง)

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

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

ฉันไม่แน่ใจว่าฉันทำอะไรผิด ความคิดใด ๆ


ฉันรู้ว่านี่เป็นคำถามเก่า แต่ ... คุณไม่ควรทิ้งบริบท db ในบริการ บริบท db จะถูกกำจัดโดยตัวแก้ไขขอบเขตโดยอัตโนมัติ หากคุณจำหน่ายภายในบริการอาจถูกกำจัดเมื่อเรียกบริการถัดไปภายในคำขอ / ขอบเขตเดียวกัน
Silvermind

1
ตรวจสอบให้แน่ใจบริการ (ชั้นหายไป) จะเพิ่มการใช้'services.AddTransient <YourClassOrInterface> (); '
เมาริซิโอกราเซียร์เรซ

คำตอบ:


292

ข้อยกเว้นบอกว่ามันไม่สามารถแก้ปัญหาการบริการได้WebApplication1.Data.BloggerRepositoryเนื่องจากตัวสร้างในตัวควบคุมของคุณกำลังขอคลาสคอนกรีตแทนอินเทอร์เฟซ ดังนั้นเพียงแค่เปลี่ยน:

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

7
น่าทึ่งว่ามันง่ายที่จะมองข้ามตัวละครเดียว ... ขอบคุณ!
jleach

ช่างเป็นแชมป์ที่ได้รับสิ่งนี้เมื่อใช้HttpContextAccessorคลาสกลายเป็นว่าฉันต้องการIHttpContextAccessor
mtbennett

หงุดหงิดมากเพราะฉันเอวมากกว่า 30 นาทีสำหรับเรื่องนี้หรือ VS บน Mac ให้ข้อผิดพลาด "donet ออกจากที่ไม่คาดคิด" ต้องทำงานบน terminal เพื่อรับข้อผิดพลาดที่ถูกต้องจากนั้นฉันชนเข้ากับโซลูชันนี้
NoloMokgosi

57

ฉันพบปัญหานี้เนื่องจากในการตั้งค่าการฉีดที่ขึ้นต่อกันฉันพลาดการอ้างอิงของที่เก็บซึ่งเป็นการพึ่งพาตัวควบคุม:

services.AddScoped<IDependencyOne, DependencyOne>();    <-- I was missing this line!
services.AddScoped<IDependencyTwoThatIsDependentOnDependencyOne, DependencyTwoThatIsDependentOnDependencyOne>();

แก้ปัญหาให้ฉันแล้วนี่เป็นปัญหาของฉัน
anisanwesley

แก้ไขปัญหาของฉันเพราะฉันจำได้ว่าบริการของฉันไม่ได้อยู่ใน "namespace" ที่ถูกต้อง
user2982195

24

ในกรณีของฉันฉันกำลังพยายามทำการฉีดพึ่งพาสำหรับวัตถุที่จำเป็นต้องมีข้อโต้แย้งของตัวสร้าง ในกรณีนี้ระหว่างการเริ่มต้นฉันเพิ่งระบุอาร์กิวเมนต์จากไฟล์กำหนดค่าตัวอย่างเช่น

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

18

ฉันมีปัญหาที่แตกต่างกันและใช่ตัวสร้างแบบกำหนดพารามิเตอร์สำหรับตัวควบคุมของฉันถูกเพิ่มไปแล้วด้วยส่วนต่อประสานที่ถูกต้อง สิ่งที่ฉันทำคือสิ่งที่ตรงไปตรงมา ฉันเพิ่งไปที่startup.csไฟล์ของฉันซึ่งฉันสามารถเห็นวิธีการโทรเพื่อลงทะเบียน

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

ในกรณีของฉันนี้วิธีการอยู่ในระดับที่แยกต่างหากRegister Injectorดังนั้นฉันจึงต้องเพิ่มอินเทอร์เฟซที่เพิ่งเปิดตัวใหม่ที่นั่น

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

ถ้าคุณเห็นพารามิเตอร์ของฟังก์ชั่นนี้คือ this IServiceCollection

หวังว่านี่จะช่วยได้


นี่คือสิ่งที่ฉันลืมเพิ่ม ฉันพลาดการอ้างอิง Injector ไปยังบริการ ต้องการ. AddTransient <> (); ขอบคุณทุกคน!
Omzig

14

เฉพาะเมื่อทุกคนมีสถานการณ์เดียวกันเช่นฉันฉันกำลังทำกวดวิชาของ EntityFramework กับฐานข้อมูลที่มีอยู่ แต่เมื่อบริบทฐานข้อมูลใหม่ถูกสร้างขึ้นในโฟลเดอร์รุ่นเราจำเป็นต้องอัปเดตบริบทในการเริ่มต้น แต่ไม่เพียง แต่ในบริการ AddDbContext แต่ AddIdentity เช่นกันหากคุณมีการตรวจสอบผู้ใช้

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

services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<NewDBContext>()
                .AddDefaultTokenProviders();

7

คุณต้องเพิ่มบริการใหม่สำหรับDBcontextในการเริ่มต้น

ค่าเริ่มต้น

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

เพิ่มสิ่งนี้

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


7
Public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IEventRepository, EventRepository>();           
}

คุณลืมเพิ่ม "services.AddScoped" ในConfigureServicesวิธีการเริ่มต้น


5

ฉันได้รับปัญหานี้เนื่องจากความผิดพลาดที่ค่อนข้างโง่ ฉันลืมที่จะขอขั้นตอนการกำหนดค่าบริการเพื่อค้นหาคอนโทรลเลอร์โดยอัตโนมัติในแอปพลิเคชัน ASP.NET Core

การเพิ่มวิธีนี้แก้ไขได้:

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

5

ฉันต้องเพิ่มบรรทัดนี้ใน ConfigureServices เพื่อให้ทำงานได้

services.AddSingleton<IOrderService, OrderService>();

3

ฉันได้รับข้อยกเว้นด้านล่าง

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

เพราะฉันต้องการให้ Factory Factory สร้างอินสแตนซ์ของคลาส DbContext Derived IBlogContextFactory และใช้วิธีสร้างอินสแตนซ์ของอินสแตนซ์ของบริบทบล็อกเพื่อที่ฉันจะได้ใช้รูปแบบด้านล่างพร้อมกับการฉีดพึ่งพาและยังสามารถใช้การเยาะเย้ยสำหรับการทดสอบหน่วย

รูปแบบที่ฉันต้องการใช้คือ

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

แต่แทนที่จะเป็น BloggingContext () ใหม่ฉันต้องการ Inject factory ผ่าน Constructor ดังที่แสดงไว้ด้านล่างของชั้น 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;
    }
}

นี่คือรหัสลงทะเบียนบริการของฉัน

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

และด้านล่างเป็นรุ่นและคลาสโรงงานของฉัน

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

----- เพื่อแก้ไขปัญหานี้ใน. net Core MVC project - ฉันได้เปลี่ยนแปลงการลงทะเบียนด้านล่าง

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

ในระยะสั้นใน. นักพัฒนาหลักสุทธิมีหน้าที่ฉีดฟังก์ชั่นโรงงานซึ่งในกรณีของ Unity และ. Net Framework ได้รับการดูแล


3

ปัญหานี้เกิดขึ้นเนื่องจากคุณไม่ได้ลงทะเบียนส่วนประกอบการเข้าถึงข้อมูลด้วยอินเทอร์เฟซที่เขียนขึ้น ลองใช้ดังนี้

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

2

หากคุณกำลังใช้ AutoFac และรับข้อผิดพลาดนี้คุณควรเพิ่มคำสั่ง "เป็น" เพื่อระบุบริการที่การใช้งานเป็นรูปธรรมนั้น

กล่าวคือ คุณควรเขียน:

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

แทน

containerBuilder.RegisterType<DataService>();

2

โอ้ขอบคุณ @kimbaudi ฉันทำตามนี้

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

และได้รับข้อผิดพลาดเดียวกับของคุณ แต่หลังจากอ่านรหัสของคุณฉันพบว่าโซลูชันของฉันเพิ่มขึ้น

services.AddScoped (IGenericRepository, GenericRepository);

เข้าสู่วิธีการConfigureServicesในไฟล์ StartUp.cs =))


2

การแก้ไขการบริการเสร็จสิ้นก่อนที่จะถึงรหัสชั้นเรียนดังนั้นเราจึงต้องตรวจสอบการฉีดพึ่งพาของเรา

ในกรณีของฉันฉันเพิ่ม

        services.AddScoped<IMeasurementService, MeasurementService>();

ใน StartupExtensions.cs


1

เพิ่มบริการ AddSingleton (); ในวิธี ConfigureServices ของไฟล์ Startup.cs ของโครงการของคุณ

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

สำหรับรายละเอียดเพิ่มเติมกรุณาเยี่ยมชม URL นี้: https://www.youtube.com/watch?v=aMjiiWtfj2M

สำหรับวิธีการทั้งหมด (เช่น AddSingleton vs AddScoped vs AddTransient) โปรดไปที่ URL นี้: https://www.youtube.com/watch?v=v6Nr7Zman_Y&list=PL6n9fhu94yhVkdrusLaQsfERmL_Jh4XmU&index=44 )


1

ฉันมีปัญหาเดียวกันและพบว่ารหัสของฉันใช้การฉีดก่อนที่จะเริ่มต้นได้

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

ฉันรู้ว่ามันไม่มีอะไรเกี่ยวข้องกับคำถาม แต่เนื่องจากฉันถูกส่งไปที่หน้านี้ฉันก็เข้าใจว่ามันจะเป็นประโยชน์กับคนอื่น


0

ฉันแทนที่

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

กับ

services.AddTransient<IMyLogger, MyLogger>();

และมันก็ได้ผลกับฉันด้วย


-1

ฉันได้รับข้อผิดพลาดนี้เพราะฉันประกาศตัวแปร (เหนือวิธี ConfigureServices) ประเภทที่เป็นบริบทของฉัน ฉันมี:

CupcakeContext _ctx

ไม่แน่ใจว่าฉันคิดอะไรอยู่ ฉันรู้ว่าการทำเช่นนี้เป็นการกระทำที่ถูกต้องหากคุณส่งผ่านพารามิเตอร์ไปยังวิธีการกำหนดค่า


-1

ฉันได้รับข้อผิดพลาด: "ไม่สามารถแก้ไขการพึ่งพา xxxxxxxx สำหรับ. net core ทุกรุ่น" ฉันลองทุกอย่างที่มีในอินเทอร์เน็ตและติดอยู่หลายวัน ทางออกเดียวที่ฉันเกิดขึ้นคือเพิ่มไฟล์ nuget.config ในโครงการแล้วใช้ dotnet restore เพื่อทำให้มันใช้งานได้

เนื้อหาของไฟล์ 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>
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.