从 AddTransient 到 AddScoped 的 ASP.NET Core 依赖项注入

Posted

技术标签:

【中文标题】从 AddTransient 到 AddScoped 的 ASP.NET Core 依赖项注入【英文标题】:ASP.NET Core dependency injection from AddTransient to AddScoped 【发布时间】:2019-12-19 03:23:55 【问题描述】:

我正在查看 ASP.NET Core 应用程序中的一些代码,我注意到存储库服务是使用 AddScope 添加的,而在我的中我使用的是 addTransient。我已经查看了差异,但我仍然不明白这将如何改变我的实际应用程序。

这是我正在使用的代码以及我在页面上用于获取/发布操作的服务。

    services.AddHttpContextAccessor();
    services.AddSingleton<IFileProvider>(new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/files")));
    services.AddTransient<IAuthorizationHandler, HasArranqueActivoHandler>();
    services.AddTransient<IAuthorizationHandler, HasArranqueInactivoHandler>();
    services.AddTransient<IAuthorizationHandler, IsParagemNotOnGoingHandler>();
    services.AddTransient<IAuthorizationHandler, IsParagemOnGoingHandler>();


    services.AddTransient<Services.Interfaces.IUserService, Services.UserService>();


        #region AreaProduction
        services.AddTransient<Production.Interfaces.IComponenteService, Production.ComponenteService>();
        services.AddTransient<Production.Interfaces.IReferenciaService, Production.ReferenciaService>();
        services.AddTransient<Production.Interfaces.IProducaoRegistoService, Production.ProducaoRegistoService>();
        services.AddTransient<Production.Interfaces.IParagemService, Production.ParagemService>();
        services.AddTransient<Production.Interfaces.ICelulaService, Production.CelulaService>();
        services.AddTransient<Production.Interfaces.IUapService, Production.UapService>();
        services.AddTransient<Production.Interfaces.ICelulaTipoService, CelulaTipoService>();
        services.AddTransient<Production.Interfaces.IMatrizService, MatrizService>();
        services.AddTransient<Production.Interfaces.IOperadorService, Production.OperadorService>();
        services.AddTransient<Production.Interfaces.IEtiquetaService, Production.EtiquetaService>();
        services.AddTransient<Production.Interfaces.IPokayokeService, Production.PokayokeService>();
        services.AddTransient<Production.Interfaces.IGeometriaService, Production.GeometriaService>();
        services.AddTransient<Production.Interfaces.IEmpregadoService, Production.EmpregadoService>();
        services.AddTransient<Production.Interfaces.IPecaService, Production.PecaService>();
        services.AddTransient<Production.Interfaces.IDefeitoService, Production.DefeitoService>();
        #endregion

        #region AreaRobotics
        services.AddTransient<Robotics.Interfaces.ITurnoService, Robotics.TurnoService>();
        services.AddTransient<Robotics.Interfaces.ICelulaService, Robotics.CelulaService>();
        services.AddTransient<Robotics.Interfaces.ICheckListService, Robotics.CheckListService>();
        services.AddTransient<Robotics.Interfaces.IProducaoService, Robotics.ProducaoService>();
        services.AddTransient<Robotics.Interfaces.IReferenciaService, Robotics.ReferenciaService>();
        services.AddTransient<Robotics.Interfaces.IDefeitoService, Robotics.DefeitoService>();
        services.AddTransient<Robotics.Interfaces.IDefeitoCodigoService, Robotics.DefeitoCodigoService>();
        services.AddTransient<Robotics.Interfaces.IParagemService, Robotics.ParagemService>();
        services.AddTransient<Robotics.Interfaces.IParagemCodigoService, Robotics.ParagemCodigoService>();
        services.AddTransient<Robotics.Interfaces.IPecaService, Robotics.PecaService>();
        services.AddTransient<Robotics.Interfaces.IUapService, Robotics.UapService>();
        services.AddTransient<Robotics.Interfaces.IOperadorService, Robotics.OperadorService>();
        #endregion

    #region AreaRobotics
    services.AddTransient<Areas.Robotics.Services.Interfaces.ITurnoService, Areas.Robotics.Services.TurnoService>();
    services.AddTransient<Areas.Robotics.Services.Interfaces.ICelulaService, Areas.Robotics.Services.CelulaService>();
    services.AddTransient<Areas.Robotics.Services.Interfaces.ICheckListService, Areas.Robotics.Services.CheckListService>();
    services.AddTransient<Areas.Robotics.Services.Interfaces.IProducaoService, Areas.Robotics.Services.ProducaoService>();
    services.AddTransient<Areas.Robotics.Services.Interfaces.IReferenciaService, Areas.Robotics.Services.ReferenciaService>();
    services.AddTransient<Areas.Robotics.Services.Interfaces.IDefeitoService, Areas.Robotics.Services.DefeitoService>();
    services.AddTransient<Areas.Robotics.Services.Interfaces.IDefeitoCodigoService, Areas.Robotics.Services.DefeitoCodigoService>();
    services.AddTransient<Areas.Robotics.Services.Interfaces.IParagemService, Areas.Robotics.Services.ParagemService>();
    services.AddTransient<Areas.Robotics.Services.Interfaces.IParagemCodigoService, Areas.Robotics.Services.ParagemCodigoService>();
    services.AddTransient<Areas.Robotics.Services.Interfaces.IPecaService, Areas.Robotics.Services.PecaService>();
    services.AddTransient<Areas.Robotics.Services.Interfaces.IUapService, Areas.Robotics.Services.UapService>();
    services.AddTransient<Areas.Robotics.Services.Interfaces.IOperadorService, Areas.Robotics.Services.OperadorService>();
    #endregion

我想知道从 AddTransient 更改为 AddScoped 对我的应用程序中的服务有何影响

这是一个简单的创建页面,其中包含许多用于填充选择列表的临时服务

    public class CreateModel : PageModel
    
        private readonly IMatrizService _matrizService;
        private readonly IReferenciaService _referenciaService;
        private readonly IUapService _uapService;
        private readonly ICelulaTipoService _celulaTipoService;
        private readonly ICelulaService _celulaService;
        private readonly IToastNotification _toastNotification;
        private readonly ILogger<CreateModel> _logger;

        public CreateModel(IToastNotification toastNotification, 
                           ICelulaTipoService celulaTipoService,
                           ICelulaService celulaService,
                           IUapService uapService,
                           IReferenciaService referenciaService,
                           IMatrizService matrizService,
                           ILogger<CreateModel> logger)
        
            _matrizService = matrizService;
            _referenciaService = referenciaService;
            _uapService = uapService;
            _celulaTipoService = celulaTipoService;
            _celulaService = celulaService;
            _toastNotification = toastNotification;
            _logger = logger;
        

        [BindProperty]
        public Matriz Matriz  get; set; 

        public void OnGet()
        
            ViewData["CelulaId"] = new SelectList(_celulaService.GetAllFromCache(), "Id", "Nome");
            ViewData["UAPId"] = new SelectList(_uapService.GetAllFromCache(), "Id", "Nome");
            ViewData["ReferenciaId"] = new SelectList(_referenciaService.GetAllFromCache(), "Id", "Nome");
        

        public async Task<IActionResult> OnPostAsync()
        
            if (!ModelState.IsValid)
            
                ViewData["CelulaId"] = new SelectList(_celulaService.GetAllFromCache(), "Id", "Nome");
                ViewData["UAPId"] = new SelectList(_uapService.GetAllFromCache(), "Id", "Nome");
                ViewData["ReferenciaId"] = new SelectList(_referenciaService.GetAllFromCache(), "Id", "Nome");

                _toastNotification.AddErrorToastMessage("Falha ao criar Matriz. Verifique os dados novamente.");

                return Page();
            

            _matrizService.Add(Matriz);

            try
            
                await _matrizService.SaveChangesAsync();
            
            catch (DbUpdateException e)
            
                //This either returns a error string, or null if it can’t handle that error
                _logger.LogError($@"Falha ao Adicionar Referência
                                    Area: Administration
                                    Page: Account/Robotics/Referencias/Create
                                    Error: e.InnerException");

                _toastNotification.AddErrorToastMessage($"A Matriz com a Referência Matriz.Referencia.Nome já existe");

                return Page();
            

            _toastNotification.AddSuccessToastMessage("Matriz criada com sucesso.");

            return RedirectToPage("./Index");
        
    

我的大部分服务通常与 crud 方法相同

  public interface IUserService
    
        IQueryable<User> GetAll();
        User GetById(int id);
        void Add(User user);
        void Update(User user);
        int AddAndSave(User user);
        int SaveChanges();
        int UpdateDateAndSave(User user);
        User GetByUsername(string username);
    

public class UserService : IUserService

    private readonly DatabaseContext _context;

    public UserService(DatabaseContext context)
    
        _context = context;
    

    public void Add(User user)
    
        _context.Users.Add(user);
    

    public int AddAndSave(User user)
    
        _context.Users.Add(user);
        return _context.SaveChanges();
    

    public IQueryable<User> GetAll()
    
        return _context.Users.AsNoTracking();
    

    public User GetById(int id)
    
        return _context.Users.Find(id);
    

    public User GetByUsername(string username)
    
        return _context.Users
            .Include(u => u.Colaborador)
            .Include(u => u.UserRoles)
            .ThenInclude(ur => ur.Role)
            .AsNoTracking()
            .FirstOrDefault(u => u.Username == username);
    

    public int SaveChanges()
    
        return _context.SaveChanges();
    

    public void Update(User user)
    
        _context.Users.Update(user);
    

    public int UpdateDateAndSave(User user)
    
         user.DataSessao = DateTime.Now;
        _context.Users.Attach(user);
        _context.Entry(user).Property(u => u.DataSessao).IsModified = true;
        return _context.SaveChanges();

    

【问题讨论】:

来自docs:每个客户端请求(连接)都会创建一次作用域生命周期服务(AddScoped)。每次从服务容器请求它们时,都会创建瞬态生命周期服务 (AddTransient)。您的实际问题是什么? AddTransient, AddScoped and AddSingleton Services Differences?的可能重复 在不了解每个正在注册/实现的依赖项的详细信息的情况下,实际上不可能评估该更改将如何影响您的整个应用程序。但是了解 Scoped 与 Transient 生活方式之间的区别(基于之前的 cmets),您应该能够查看您的依赖关系并确定如何适当地配置它们的生活方式。也许只提供一个包含您的一项服务的特定示例会更好地确定问题的范围 小问题,你不能在这个文件中添加using Areas.Robotics.Services;,它会让代码更具可读性:) 我们仍然无法为这个示例提供帮助。 需要决定的关键是依赖关系会发生什么以及它们如何相互交互。例如,如果两个依赖项都具有第三个依赖项,那么第三项需要是不同的对象还是可以共享。 【参考方案1】:

如果我开始将 AddScoped 用于服务,这将如何影响我的整个应用程序

我们必须查看一项服务(或者实际上是所有服务)才能确定。

但很可能这无关紧要。

需要注意的是服务中的状态。通常没有状态,然后 Scoped 或 Transient 无关紧要。

那么,您的任何服务实现是否有字段(注入服务除外)?

当它们当前注册为 Transient 时,它们很可能是无状态的。这使得对 Scoped 的更改是安全的,但仍然没有必要。

换一种方式(从 Scoped 到 Transient)需要更多的调查。

【讨论】:

我用我使用的服务更新了我的帖子,我的大部分服务都是这样的 您的DatabaseContext 可能是唯一重要的服务。它应该是 Scoped 的。 那个是使用 AddDbContext 添加的 并且 AddDbContext 知道它必须被限定。我们应该可以更好地访问所有这些注册助手的文档。 不,所有其他服务都可以而且应该是瞬态的(除非他们有特殊的理由不这样做)。我根本不建议切换到 Scoped。另外,不要修复任何没有损坏的东西。

以上是关于从 AddTransient 到 AddScoped 的 ASP.NET Core 依赖项注入的主要内容,如果未能解决你的问题,请参考以下文章

使用 IServiceCollection.AddTransient、IServiceCollection.AddSingleton 和 IServiceCollectionAddScoped 方法的

使用 IServiceCollection.AddTransient、IServiceCollection.AddSingleton 和 IServiceCollectionAddScoped 方法的

AddTransientAddSingletonAddScoped的区别

AddTransientAddSingletonAddScoped的区别

AddTransientAddSingletonAddScoped的区别

无法从 ViewComponent 中解析缓存服务