Elv*_*dov 746 c# .net-core asp.net-core
我想要实现dependency injection在Asp.Net Core.因此,在将此代码添加到ConfigureServices方法之后,两种方式都有效.
services.AddTransient和service.AddScoped方法有Asp.Net Core什么区别?
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddScoped<IEmailSender, AuthMessageSender>();
}
Run Code Online (Sandbox Code Playgroud)
aka*_*mis 1320
TL; DR
瞬态物体总是不同的; 为每个控制器和每个服务提供一个新实例.
范围内的对象在请求中是相同的,但在不同的请求中是不同的.
Singleton对象对于每个对象和每个请求都是相同的.
有关更多说明,来自asp.net docs的此示例显示了不同之处:
为了演示这些生命周期和注册选项之间的区别,请考虑一个简单的接口,它将一个或多个任务表示为具有唯一标识符的操作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
{
}
}
Run Code Online (Sandbox Code Playgroud)
我们使用单个类来实现这些接口Operation,它Guid在其构造函数中接受a ,或者Guid如果没有提供则使用new .
接下来,在ConfigureServices每个类型根据其命名生命周期添加到容器中:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,该IOperationSingletonInstance服务正在使用具有已知ID的特定实例,Guid.Empty因此在使用此类型时将会很清楚.我们还注册了一个OperationService取决于其他每种Operation类型的内容,以便在请求中清楚地知道该服务是为每个操作类型获得与控制器相同的实例,还是新实例.所有这些服务都将其依赖项公开为属性,因此它们可以显示在视图中.
services.AddTransient<IOperationTransient, Operation>();
services.AddScoped<IOperationScoped, Operation>();
services.AddSingleton<IOperationSingleton, Operation>();
services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));
services.AddTransient<OperationService, OperationService>();
Run Code Online (Sandbox Code Playgroud)
为了演示对应用程序的单独单个请求内和之间的对象生存期,该示例包括OperationsController请求每种IOperation类型以及a 的类型OperationService.Index然后,该操作将显示所有控制器和服务的OperationId值.
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
观察OperationId请求中和请求之间的哪些值有所不同.
瞬态物体总是不同的; 为每个控制器和每个服务提供一个新实例.
范围内的对象在请求中是相同的,但在不同的请求中是不同的
Singleton对象对于每个对象和每个请求都是相同的(无论是否提供实例ConfigureServices)
aka*_*mis 288
在dotnet的依赖注入中,有三个主要的生命周期:
Singleton在整个应用程序中创建单个实例.它首次创建实例,并在所有调用中重用相同的对象.
作用域是每个范围内的请求曾经创造终身服务.它相当于当前范围内的Singleton.例如.在MVC中,它为每个http请求创建1个实例,但在同一Web请求中的其他调用中使用相同的实例.
每次请求时都会创建瞬态生命周期服务.这种生命周期最适合轻量级无状态服务.
在这里你可以找到和看到差异的例子:
http://dotnetliberty.com/index.php/2015/10/15/asp-net-5-mvc6-dependency-injection-in-6-steps/
https://codewala.net/2015/04/30/your-dependency-injection-ready-asp-net-asp-net-5/
这是官方文档的链接:
ber*_*gle 146
短暂的
有范围的
单身人士
在需要维护应用程序范围状态的地方使用单例。应用程序配置或参数、日志服务、数据缓存是您可以使用单例的一些示例。
Ham*_*aei 32
这张图片很好地说明了这个概念。不幸的是,我找不到这张图片的原始来源,但有人制作了它,他以图片的形式很好地展示了这个概念。

Shi*_*ala 27
当必须注入多个相同类型的对象时,瞬态,作用域和单例定义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
}
}
Run Code Online (Sandbox Code Playgroud)
瞬态: - 在瞬态新对象实例中将注入一个请求和响应.下面是我显示GUID值的快照图像.
范围: - 在范围内相同的对象实例将被注入单个请求和响应.
Singleton: - 在Singleton中,将在所有请求和响应中注入相同的对象.在这种情况下,将创建一个对象的全局实例.
下面是一个简单的图表,它直观地解释了上述基本原理
上面的图片由SBSS团队绘制,当我在孟买培训中进行ASP.NET MVC培训时,非常感谢SBSS团队创建上述图像.
use*_*177 27
通常,代码请求应该通过构造函数参数进行,如
public MyConsumingClass(IDependency dependency)
Run Code Online (Sandbox Code Playgroud)
我想在@ akazemis的回答中指出DI的上下文中的"服务"并不意味着RESTful服务; 服务是提供功能的依赖项的实现.
Off*_*'er 20
在寻找这个问题的答案后,我找到了一个很好的解释,我想与你分享一个例子。
您可以在此处观看演示差异的视频
在这个例子中,我们有这个给定的代码:
public interface IEmployeeRepository
{
IEnumerable<Employee> GetAllEmployees();
Employee Add(Employee employee);
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
}
public class MockEmployeeRepository : IEmployeeRepository
{
private List<Employee> _employeeList;
public MockEmployeeRepository()
{
_employeeList = new List<Employee>()
{
new Employee() { Id = 1, Name = "Mary" },
new Employee() { Id = 2, Name = "John" },
new Employee() { Id = 3, Name = "Sam" },
};
}
public Employee Add(Employee employee)
{
employee.Id = _employeeList.Max(e => e.Id) + 1;
_employeeList.Add(employee);
return employee;
}
public IEnumerable<Employee> GetAllEmployees()
{
return _employeeList;
}
}
Run Code Online (Sandbox Code Playgroud)
家庭控制器
public class HomeController : Controller
{
private IEmployeeRepository _employeeRepository;
public HomeController(IEmployeeRepository employeeRepository)
{
_employeeRepository = employeeRepository;
}
[HttpGet]
public ViewResult Create()
{
return View();
}
[HttpPost]
public IActionResult Create(Employee employee)
{
if (ModelState.IsValid)
{
Employee newEmployee = _employeeRepository.Add(employee);
}
return View();
}
}
Run Code Online (Sandbox Code Playgroud)
创建视图
@model Employee
@inject IEmployeeRepository empRepository
<form asp-controller="home" asp-action="create" method="post">
<div>
<label asp-for="Name"></label>
<div>
<input asp-for="Name">
</div>
</div>
<div>
<button type="submit">Create</button>
</div>
<div>
Total Employees Count = @empRepository.GetAllEmployees().Count().ToString()
</div>
</form>
Run Code Online (Sandbox Code Playgroud)
启动文件
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton<IEmployeeRepository, MockEmployeeRepository>();
}
Run Code Online (Sandbox Code Playgroud)
复制粘贴此代码,然后按上的观点和开关按钮创建
AddSingleton,AddScoped并且AddTransient每一次你会得到不同的结果,这将有助于你了解这一点。
AddSingleton() - 顾名思义,AddSingleton() 方法创建一个单例服务。单例服务是在第一次请求时创建的。然后所有后续请求都使用相同的实例。因此,一般来说,每个应用程序只创建一次 Singleton 服务,并且在整个应用程序生命周期中都使用该单个实例。
AddTransient() - 此方法创建一个瞬态服务。每次请求时都会创建一个 Transient 服务的新实例。
AddScoped() - 此方法创建一个 Scoped 服务。作用域内的每个请求都会创建一个新的作用域服务实例。例如,在 Web 应用程序中,它为每个 http 请求创建 1 个实例,但在同一 Web 请求内的其他调用中使用相同的实例。
小智 17
添加到这里所有精彩的答案。这个答案将解释不同的生命周期以及何时适合选择它们。与软件开发中的许多事情一样,没有绝对的事情,许多条件都会影响最佳选择,因此请将此答案视为一般指导。
\nASP.NET Core 附带了自己的内置依赖注入容器,它使用它来解析请求生命周期中所需的服务。所有框架服务,例如日志记录、配置、路由等,都使用依赖注入,并且在构建应用程序主机时将它们注册到依赖注入容器中。在内部,ASP.NET Core 框架提供激活框架组件(例如控制器和 Razor 页面)时所需的依赖项。
\n依赖注入容器(有时称为控制反转或 IoC 容器)是管理对象实例化和配置的软件组件。依赖注入容器并不是应用依赖注入模式的必要条件,但随着应用程序的增长,使用依赖注入容器可以极大地简化依赖项的管理,包括它们的生命周期。服务在启动时向容器注册,并在运行时在需要时从容器解析。容器负责创建和处置所需服务的实例,并在指定的生命周期内维护它们。
\n使用 Microsoft 依赖注入容器时,我们主要针对两个接口进行编码。
\n当向容器注册服务时,应该为服务选择服务生命周期。服务生命周期控制已解析对象在容器创建后的生存时间。注册服务时,可以通过在 IServiceCollection 上使用适当的扩展方法来定义生命周期。Microsoft 依赖注入容器可以使用三个生命周期。
\n依赖注入容器跟踪它创建的所有服务实例,一旦它们的生命周期结束,它们就会被处理或释放以进行垃圾回收。所选择的生命周期会影响同一服务实例是否可以被解析并注入到多个依赖的消费者中。因此,明智地选择服务的生命周期至关重要。
\n临时服务
\n当服务注册为 Transient 时,每次解析该服务时,容器都会创建并返回该服务的新实例。换句话说,每个通过容器注入接受 Transient 服务的依赖类都将收到自己唯一的实例。因为每个依赖类都会接收自己的实例,所以实例上的方法可以安全地改变内部状态,而不必担心其他使用者和线程的访问。
\n临时服务的用途/特征
\n单例服务
\n使用单例生命周期注册的应用程序服务在依赖注入容器的生命周期内只会创建一次。在 ASP.NET Core 中,这相当于应用程序的生命周期。第一次需要服务时,容器将创建一个实例。之后,可以重用同一实例并将其注入到所有依赖类中。该实例将在容器的生命周期内保持可达状态,因此不需要处置或垃圾收集。
\n单例服务的用途/特点
\n范围服务
\n作用域服务处于 Transient 和 Singleton 之间的中间地带。作用域服务的实例的生存期与解析它的作用域的长度相同。在 ASP.NET Core 中,应用程序中会为其处理的每个请求创建一个作用域。任何范围服务都将为每个范围创建一次,因此它们的行为与单例服务类似,但在范围的上下文中。在处理特定请求时,所有框架组件(例如中间件和 MVC 控制器)都会获取作用域服务的相同实例。
\n范围服务的用途/特征
\n避免强制依赖
\n注册依赖项时,考虑到服务本身具有的任何依赖项,确保选择的生命周期是适当的至关重要。这是必要的,以避免所谓的“强制依赖”,即服务的生存时间可能比预期的要长。
\n经验法则是,服务不应依赖于生命周期比其自身短的服务。例如,使用单例生命周期注册的服务不应依赖于瞬态服务。这样做会导致瞬态服务被单例服务捕获,并在应用程序的生命周期中无意中引用该实例。这可能会导致出现问题,有时甚至难以追踪运行时错误和行为,例如意外地在线程之间共享非线程安全服务或允许对象超过其预期生命周期。\n为了形象化这一点,让我们考虑哪些生命周期可以安全地依赖使用另一个生命周期的服务。
\nMar*_*ese 13
DI 容器一开始可能非常令人困惑,尤其是在生命周期方面。毕竟,容器使用反射来使一切“正常工作”。它有助于思考容器实际上在幕后为您完成什么:组成对象图。
对于 .NET Web 应用程序,使用 DI 容器的替代方法是将默认控制器激活器替换为您自己的控制器激活器,该激活器必须管理生命周期并手动构建依赖关系图。出于学习目的,假设您有一个控制器激活器,该激活器经过硬编码以在每次出现 Web 请求时返回一个特定控制器:
// This class is created once per application during startup. In DI terms, it is the
// "composition root."
public class DumbControllerActivator
{
// Shared among all consumers from all requests
private static readonly Singleton1 singleton1 = new Singleton1();
private static readonly Singleton2 singleton2 = new Singleton2();
// This method's responsibility is to construct a FooController and its dependecies.
public FooController HandleFooRequest()
{
// Shared among all consumers in this request
var scoped1 = new Scoped1();
var scoped2 = new Scoped2(singleton1, scoped1);
return new FooController(
singleton1,
scoped1,
new Transient1( // Fresh instance
singleton2,
new Transient2(scoped2)), // Fresh instance
new Transient3( // Fresh instance
singleton1,
scoped1,
new Transient1( // Fresh instance
singleton2,
new Transient2(scoped2))); // Fresh instance
}
}
Run Code Online (Sandbox Code Playgroud)
为了更深入地了解 DI,我强烈推荐《依赖注入原理、实践和模式》一书。我的回答基本上只是重复我在那里学到的东西。
有很棒的答案。我想通过类比来简单介绍一下。
想象一下,您经营一家咖啡店,拥有三种类型的员工:咖啡师、收银员和经理。每种类型的员工代表咖啡店应用程序中具有不同生命周期的不同依赖关系。
短暂的生命周期(咖啡师):
services.AddTransient<IBarista, Barista>();
Run Code Online (Sandbox Code Playgroud)
范围生命周期(出纳员):
services.AddScoped<ICashier, Cashier>();
Run Code Online (Sandbox Code Playgroud)
单例生命周期(经理):
services.AddSingleton<IManager, Manager>();
Run Code Online (Sandbox Code Playgroud)
现在,让我们应用这个类比来从数据库中检索项目:
瞬态生命周期(咖啡师):如果将服务配置为瞬态,则每次需要时都会创建该服务的新实例。在数据库上下文中,为每个数据库查询创建一个新的数据库连接或数据访问组件。查询完成后,该连接将被关闭并释放。
作用域生命周期(Cashiers):如果将服务配置为作用域,则在操作或请求期间将创建该服务的单个实例。在数据库上下文中,创建单个数据库连接或数据访问组件,并在同一操作或请求内的所有查询之间共享。一旦操作或请求完成,连接就会关闭并被释放。
单例生命周期(管理器):如果将一项服务配置为单例,则在整个应用程序的生命周期中仅创建和共享该服务的一个实例。在数据库上下文中,将创建单个数据库连接或数据访问组件,并将其用于从应用程序启动到关闭的所有查询。
选择使用哪个生命周期取决于特定应用场景中的资源效率、隔离要求和并发注意事项等因素。不同的生命周期提供不同的权衡,因此必须选择符合应用程序要求和性能限制的生命周期。
除了这些信息之外,我还构建了一个网络项目来根据其他用户提供的上述精彩信息来测试它们。
这是网络应用程序的结果:
正如您在图片中看到的,两个不同的请求Index和,为瞬态和作用域Privacy返回不同的结果。
如果有人想知道这些代码,这是我创建的应用程序。
在首次请求该服务时,AddSingleton()将创建该服务的单个实例,并在需要该服务的所有位置重用该实例。
在具有每个http请求的范围服务中,我们获得了一个新实例。但是,在同一个http请求中,如果在多个位置(如视图和控制器中)需要服务,则将为该http请求的整个范围提供相同的实例。但是,每个新的http请求都将获得该服务的新实例。
对于临时服务,每次请求服务实例时,无论是在同一http请求的范围内还是跨不同的http请求,都将提供一个新实例。
在.NET Core所有这些技术中AddTransient,AddScoped并AddSingleton定义注入对象的生命周期。
AddSingleton- 在整个应用程序或服务器中,只会创建该类的一个全局实例。
builder.Services.AddSingleton<ConsoleLogger>();
Run Code Online (Sandbox Code Playgroud)
AddScoped- 对于每个请求,都会创建不同的实例,但在同一个请求中,如果您要求依赖项注入对象两次、三次,则将提供相同的实例,所有对象都将相同。请参阅下面的示例
builder.Services.AddScoped<ConsoleLogger>();
Run Code Online (Sandbox Code Playgroud)
现在在控制器类中
ConsoleLogger _logger;
public HomeController( ConsoleLogger logger, ------ Same object
ConsoleLogger logger1) ------ Same object
{
_logger = consoleLogger;
}
Run Code Online (Sandbox Code Playgroud)
这里logger等于logger1.
AddTransient- 对于每个请求,即使在请求内,如果您多次要求 DI 创建对象,也会创建新实例,每次都会创建新实例。请参阅下面的示例
builder.Services.AddTransient<ConsoleLogger>();
Run Code Online (Sandbox Code Playgroud)
现在在控制器类中
ConsoleLogger _logger;
public HomeController( ConsoleLogger logger, ------ New object
ConsoleLogger logger1) ------ New object
{
_logger = consoleLogger;
}
Run Code Online (Sandbox Code Playgroud)
这里logger不等于logger1。希望能帮助到你。
| 归档时间: |
|
| 查看次数: |
277941 次 |
| 最近记录: |