简体   繁体   English

ASP.NET Core-在Singleton注入上存储库依赖项注入失败

[英]ASP.NET Core - Repository dependency injection fails on Singleton injection

I am using SoapCore to create a web service for my ASP.NET Core MVC application. 我正在使用SoapCore为ASP.NET Core MVC应用程序创建Web服务。

I am using Entity Framework Core and a simple Repository pattern to get my DB data. 我正在使用Entity Framework Core和简单的存储库模式来获取我的数据库数据。

I am injecting my repository classes via .AddSingleton() in my Startup.cs: 我通过Startup.cs中的.AddSingleton()注入存储库类:

services.AddSingleton<IImportRepository, ImportRepository>();
services.AddSingleton<IWebService, WebService>();

Since the EF DbContext is scoped I get an error when calling my web service: 由于EF DbContext具有作用域,因此在调用Web服务时出现错误:

Cannot consume scoped service 'App.Data.ApplicationDbContext' from singleton 'App._Repository.IImportRepository'. 无法使用单例“ App._Repository.IImportRepository”中的作用域服务“ App.Data.ApplicationDbContext”。

When I use .AddScoped() instead, it works fine. 当我改用.AddScoped() ,它可以正常工作。

I've read injecting scoped dependencies via a controllers/classes constructor is bad practice, since it "falls back" to be a singleton or behaves like one. 我读过通过controllers / classes构造函数注入作用域的依赖关系是不好的做法,因为它“回退”为单例或行为类似。

I wonder if there is another way to make it work with singletons or if this has some major draw backs in the long term (about 100-200 users will use the site) when using scoped injections in my controllers via the ctor? 我想知道是否有另一种方法可以使它与单例一起使用,或者通过ctor在我的控制器中使用作用域注入时,从长远来看是否会有一些重大缺点(大约100-200个用户将使用该站点)?

Simply put, your go-to lifetime should be "scoped". 简而言之,您的终生应该受到“限制”。 You should only use a singleton or transient lifetime if you have a good reason to do so. 仅在有充分理由的情况下才应使用单例或瞬态寿命。 For a singleton, that's stuff like managing locks or holding data that needs to persist for the lifetime of the application, neither of which applies to the concept of a repository. 对于单例来说,诸如管理锁或保存数据等在应用程序的整个生命周期中都需要持续存在的东西,都不适用于存储库的概念。 Repositories should be entirely disposable. 储存库应该完全是一次性的。 The point is to persist to the database or to some other store, so they should not contain any data in their own right that needs to be persisted. 关键是要保留到数据库或其他存储中,因此它们不应自行包含任何需要保留的数据。

Long and short, your best bet here is to simply make your repo(s) scoped, so you can directly inject the context. 总之,最好的选择是使存储库范围有限,以便您可以直接插入上下文。 As far as constructor injection goes, I'm not sure where you got the idea that that's a bad practice. 就构造函数注入而言,我不确定这是一个不好的做法。 It's in fact how dependency injection works in most cases, so you can't really have one without the other. 实际上,在大多数情况下这就是依赖注入的工作原理,因此您不能一无所有。

If you absolutely need to have a singleton, then your only option is the service locator antipattern. 如果您绝对需要单身人士,那么唯一的选择就是服务定位器反模式。 For that, you will inject IServiceProvider : 为此,您将注入IServiceProvider

public class MyRepo
{
    private readonly IServiceProvider _serviceProvider;

    public MyRepo(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    ...
}

Then, each time you need the context (that's important), you'll need to do: 然后, 每当需要上下文时 (这很重要),就需要执行以下操作:

using (var scope = _serviceProvider.CreateScope())
{
    var context = scope.ServiceProvider.GetRequiredService<MyContext>();
    // do something with context
}

Scoped objects cannot be injected in to singleton objects. 作用域对象不能注入到单例对象中。 Its simple as this, singletons are created only once when the app is starting and used by all the subsequent requests. 如此简单,单例仅在应用程序启动时创建一次,并被所有后续请求使用。 Scoped objected are created during each request and disposed at the end of the request. 范围对象在每个请求期间创建,并在请求结束时处理。 So there is no way for the previously created single object to know about the scoped objects creating during each request. 因此,先前创建的单个对象无法知道在每个请求期间创建的作用域对象。 So using scoped objects is not possible in singletons. 因此,不可能在单例中使用范围对象。 But the the other way around is possible. 但是反之亦然。

injecting scoped dependencies via a controllers/classes constructor 通过控制器/类构造器注入作用域依赖

I don't think its a bad practice at all. 我认为这不是一个坏习惯。 If not how are you planing to do unit testing? 如果不是,您打算如何进行单元测试?

Also its not right to make DB context singleton. 同样,使数据库上下文单例也是不正确的。 You will face cashing issues/data anomalies in parallel and subsequent requests. 您将同时面临现金问题/数据异常以及后续要求。 In my opinion DB context has to be scoped and all the objects that uses the db-context has to be scoped all the way up. 在我看来,必须确定数据库上下文的范围,并且必须将使用db-context的所有对象一直确定范围。

So in you case, make all the objects ImportRepository, WebService and DB Context all scoped. 因此,在所有情况下,使所有对象ImportRepository,WebService和DB Context都具有作用域。

Cheers, 干杯,

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM