我想在ASP.NET Core中实现依赖注入(DI)。因此,在将此代码添加到ConfigureServices方法后,这两种方法都可以工作。

ASP.NET Core中的services.AddTransient和service.AddScoped方法之间有什么区别?

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.

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

当前回答

TL;博士

瞬态对象总是不同的;将向提供一个新实例每个控制器和每个服务。作用域对象在请求中是相同的,但在请求中不同不同的请求。单个对象对于每个对象和每个请求都是相同的。

为了进一步澄清,.NET文档中的这个示例显示了不同之处:

要演示这些生存期和注册选项之间的区别,请考虑一个简单的接口,该接口将一个或多个任务表示为具有唯一标识符OperationId的操作。根据我们如何配置此服务的生存期,容器将向请求类提供相同或不同的服务实例。为了明确请求的生存期,我们将为每个生存期创建一个类型选项:

using System;

namespace DependencyInjectionSample.Interfaces
{
    public interface IOperation
    {
        Guid OperationId { get; }
    }

    public interface IOperationTransient : IOperation
    {
    }

    public interface IOperationScoped : IOperation
    {
    }

    public interface IOperationSingleton : IOperation
    {
    }

    public interface IOperationSingletonInstance : IOperation
    {
    }
}

我们使用单个类Operation来实现这些接口,该类在构造函数中接受GUID,如果没有提供GUID,则使用新的GUID:

using System;
using DependencyInjectionSample.Interfaces;
namespace DependencyInjectionSample.Classes
{
    public class Operation : IOperationTransient, IOperationScoped, IOperationSingleton, IOperationSingletonInstance
    {
        Guid _guid;
        public Operation() : this(Guid.NewGuid())
        {

        }

        public Operation(Guid guid)
        {
            _guid = guid;
        }

        public Guid OperationId => _guid;
    }
}

接下来,在ConfigureServices中,每个类型都会根据其命名的生存期添加到容器中:

services.AddTransient<IOperationTransient, Operation>();
services.AddScoped<IOperationScoped, Operation>();
services.AddSingleton<IOperationSingleton, Operation>();
services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));
services.AddTransient<OperationService, OperationService>();

请注意,IOperationSingletonInstance服务正在使用已知ID为Guid.Empty的特定实例,因此在使用此类型时将很清楚。我们还注册了一个OperationService,它依赖于其他每个操作类型,因此在请求中可以清楚地看到,该服务是获得与控制器相同的实例,还是为每个操作类型获得一个新的实例。此服务所做的只是将其依赖项公开为财产,以便可以在视图中显示它们。

using DependencyInjectionSample.Interfaces;

namespace DependencyInjectionSample.Services
{
    public class OperationService
    {
        public IOperationTransient TransientOperation { get; }
        public IOperationScoped ScopedOperation { get; }
        public IOperationSingleton SingletonOperation { get; }
        public IOperationSingletonInstance SingletonInstanceOperation { get; }

        public OperationService(IOperationTransient transientOperation,
            IOperationScoped scopedOperation,
            IOperationSingleton singletonOperation,
            IOperationSingletonInstance instanceOperation)
        {
            TransientOperation = transientOperation;
            ScopedOperation = scopedOperation;
            SingletonOperation = singletonOperation;
            SingletonInstanceOperation = instanceOperation;
        }
    }
}

为了演示应用程序的单独请求内和请求之间的对象生存期,示例包括一个OperationsController,它请求每种IOperation类型以及一个OperationService。索引操作然后显示所有控制器和服务的OperationId值。

using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;
using Microsoft.AspNetCore.Mvc;

namespace DependencyInjectionSample.Controllers
{
    public class OperationsController : Controller
    {
        private readonly OperationService _operationService;
        private readonly IOperationTransient _transientOperation;
        private readonly IOperationScoped _scopedOperation;
        private readonly IOperationSingleton _singletonOperation;
        private readonly IOperationSingletonInstance _singletonInstanceOperation;

        public OperationsController(OperationService operationService,
            IOperationTransient transientOperation,
            IOperationScoped scopedOperation,
            IOperationSingleton singletonOperation,
            IOperationSingletonInstance singletonInstanceOperation)
        {
            _operationService = operationService;
            _transientOperation = transientOperation;
            _scopedOperation = scopedOperation;
            _singletonOperation = singletonOperation;
            _singletonInstanceOperation = singletonInstanceOperation;
        }

        public IActionResult Index()
        {
            // ViewBag contains controller-requested services
            ViewBag.Transient = _transientOperation;
            ViewBag.Scoped = _scopedOperation;
            ViewBag.Singleton = _singletonOperation;
            ViewBag.SingletonInstance = _singletonInstanceOperation;

            // Operation service has its own requested services
            ViewBag.Service = _operationService;
            return View();
        }
    }
}

现在,对该控制器操作发出两个单独的请求:

观察请求内和请求之间的OperationId值的变化。

瞬态对象总是不同的;向每个控制器和每个服务提供新实例。作用域对象在请求中相同,但在不同的请求中不同单个对象对于每个对象和每个请求都是相同的(无论ConfigureServices中是否提供了实例)

其他回答

使用哪一个

转瞬即逝的

因为它们每次创建时都会使用更多的内存和资源,并且会对性能产生负面影响对于状态很少或没有状态的轻量级服务,请使用此选项。

范围内的

当您想要维护请求中的状态时,更好的选项。

辛格尔顿

这些服务中的内存泄漏会随着时间的推移而增加。也可以节省内存,因为它们一旦在任何地方重复使用就会被创建。

在需要维护应用程序范围状态的地方使用Singleton。应用程序配置或参数、日志服务、数据缓存是可以使用单例的一些示例。

将具有不同生存期的服务注入另一个

切勿将作用域和瞬态服务注入Singleton服务。(这有效地将瞬时或作用域服务转换为单例。)从不将瞬态服务注入作用域服务(这会将瞬态服务转换为作用域。)

在.NET的依赖注入中,有三个主要的生命周期:

在整个应用程序中创建单个实例的Singleton。它第一次创建实例,并在所有调用中重用相同的对象。

作用域内的每个请求创建一次作用域生存期服务。它相当于当前范围中的单例。例如,在MVC中,它为每个HTTP请求创建一个实例,但在同一web请求中的其他调用中使用相同的实例。

每次请求时都会创建短暂的生命周期服务。这种生命周期最适合于轻量级、无状态服务。

在这里,您可以找到和示例来了解不同之处:

ASP.NET 5 MVC6依赖注入,分6个步骤进行(由于链接无效,导致web存档链接)

依赖项注入就绪的ASP.NET:ASP.NET 5

这是官方文件的链接:

ASP.NET核心中的依赖注入

通过DbContext使用EntityFramework/Core可能是生命周期的最佳说明。

建议将DbContext和与DbContext交互的存储库连接到Scoped生存期,因为DbContext显然是一个有状态的构造。所以你不想使用Singleton,因为你最终会遇到各种并发问题。您不想使用Transient,因为DbContext不是线程安全的。记住,Transient适用于处理无状态对象/类的用例。

而且,由于大多数存储库都是由控制器调用的,所以使用作用域生存期确实很有意义。可以想象,作为事务的一部分,DbContext可以在单个操作方法期间多次调用。

本文没有直接讨论这些生存期,但对为什么Scoped生存期最适合DbContext给出了很好的解释。

https://mehdi.me/ambient-dbcontext-in-ef6/?msclkid=00251b05d01411ec8d85d232374f26d5

TL;博士

瞬态对象总是不同的;将向提供一个新实例每个控制器和每个服务。作用域对象在请求中是相同的,但在请求中不同不同的请求。单个对象对于每个对象和每个请求都是相同的。

为了进一步澄清,.NET文档中的这个示例显示了不同之处:

要演示这些生存期和注册选项之间的区别,请考虑一个简单的接口,该接口将一个或多个任务表示为具有唯一标识符OperationId的操作。根据我们如何配置此服务的生存期,容器将向请求类提供相同或不同的服务实例。为了明确请求的生存期,我们将为每个生存期创建一个类型选项:

using System;

namespace DependencyInjectionSample.Interfaces
{
    public interface IOperation
    {
        Guid OperationId { get; }
    }

    public interface IOperationTransient : IOperation
    {
    }

    public interface IOperationScoped : IOperation
    {
    }

    public interface IOperationSingleton : IOperation
    {
    }

    public interface IOperationSingletonInstance : IOperation
    {
    }
}

我们使用单个类Operation来实现这些接口,该类在构造函数中接受GUID,如果没有提供GUID,则使用新的GUID:

using System;
using DependencyInjectionSample.Interfaces;
namespace DependencyInjectionSample.Classes
{
    public class Operation : IOperationTransient, IOperationScoped, IOperationSingleton, IOperationSingletonInstance
    {
        Guid _guid;
        public Operation() : this(Guid.NewGuid())
        {

        }

        public Operation(Guid guid)
        {
            _guid = guid;
        }

        public Guid OperationId => _guid;
    }
}

接下来,在ConfigureServices中,每个类型都会根据其命名的生存期添加到容器中:

services.AddTransient<IOperationTransient, Operation>();
services.AddScoped<IOperationScoped, Operation>();
services.AddSingleton<IOperationSingleton, Operation>();
services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));
services.AddTransient<OperationService, OperationService>();

请注意,IOperationSingletonInstance服务正在使用已知ID为Guid.Empty的特定实例,因此在使用此类型时将很清楚。我们还注册了一个OperationService,它依赖于其他每个操作类型,因此在请求中可以清楚地看到,该服务是获得与控制器相同的实例,还是为每个操作类型获得一个新的实例。此服务所做的只是将其依赖项公开为财产,以便可以在视图中显示它们。

using DependencyInjectionSample.Interfaces;

namespace DependencyInjectionSample.Services
{
    public class OperationService
    {
        public IOperationTransient TransientOperation { get; }
        public IOperationScoped ScopedOperation { get; }
        public IOperationSingleton SingletonOperation { get; }
        public IOperationSingletonInstance SingletonInstanceOperation { get; }

        public OperationService(IOperationTransient transientOperation,
            IOperationScoped scopedOperation,
            IOperationSingleton singletonOperation,
            IOperationSingletonInstance instanceOperation)
        {
            TransientOperation = transientOperation;
            ScopedOperation = scopedOperation;
            SingletonOperation = singletonOperation;
            SingletonInstanceOperation = instanceOperation;
        }
    }
}

为了演示应用程序的单独请求内和请求之间的对象生存期,示例包括一个OperationsController,它请求每种IOperation类型以及一个OperationService。索引操作然后显示所有控制器和服务的OperationId值。

using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;
using Microsoft.AspNetCore.Mvc;

namespace DependencyInjectionSample.Controllers
{
    public class OperationsController : Controller
    {
        private readonly OperationService _operationService;
        private readonly IOperationTransient _transientOperation;
        private readonly IOperationScoped _scopedOperation;
        private readonly IOperationSingleton _singletonOperation;
        private readonly IOperationSingletonInstance _singletonInstanceOperation;

        public OperationsController(OperationService operationService,
            IOperationTransient transientOperation,
            IOperationScoped scopedOperation,
            IOperationSingleton singletonOperation,
            IOperationSingletonInstance singletonInstanceOperation)
        {
            _operationService = operationService;
            _transientOperation = transientOperation;
            _scopedOperation = scopedOperation;
            _singletonOperation = singletonOperation;
            _singletonInstanceOperation = singletonInstanceOperation;
        }

        public IActionResult Index()
        {
            // ViewBag contains controller-requested services
            ViewBag.Transient = _transientOperation;
            ViewBag.Scoped = _scopedOperation;
            ViewBag.Singleton = _singletonOperation;
            ViewBag.SingletonInstance = _singletonInstanceOperation;

            // Operation service has its own requested services
            ViewBag.Service = _operationService;
            return View();
        }
    }
}

现在,对该控制器操作发出两个单独的请求:

观察请求内和请求之间的OperationId值的变化。

瞬态对象总是不同的;向每个控制器和每个服务提供新实例。作用域对象在请求中相同,但在不同的请求中不同单个对象对于每个对象和每个请求都是相同的(无论ConfigureServices中是否提供了实例)

当必须注入同一类型的多个对象时,ASP.NET MVC核心DI(依赖注入)中的瞬时、作用域和单例定义对象创建过程。如果你是依赖注入的新手,你可以看到这个DI-IoC视频。

您可以看到下面的控制器代码,其中我在构造函数中请求了两个“IDal”实例。Transient、Scoped和Singleton定义了同一个实例是否将以“_dal”和“_dal1”或不同的方式注入。

public class CustomerController : Controller
{
    IDal dal = null;

    public CustomerController(IDal _dal,
                              IDal _dal1)
    {
        dal = _dal;
        // DI of MVC core
        // inversion of control
    }
}

瞬态:在瞬态中,将在单个请求和响应中注入新的对象实例。下面是我显示GUID值的快照图像。

作用域:在作用域中,相同的对象实例将被注入到单个请求和响应中。

Singleton:在Singleton中,将在所有请求和响应中注入相同的对象。在这种情况下,将创建对象的一个全局实例。

下面是一个简单的图表,直观地解释了上述基本原理。

上图是我在孟买参加ASP.NET MVC培训时,SBSS团队绘制的。非常感谢SBSS团队创建了上述图像。