Microsoft.Extensions.DependencyInjection에 대한 시간 범위 등록 메커니즘

8162 단어 dotnetprogramming
Microsoft DI(종속성 주입) 컨테이너는 세 가지 유형의 등록을 제공합니다.
  • 일시적인
  • 스코프
  • 싱글턴
  • Transient 등록은 요청 시 항상 서비스의 새 인스턴스를 생성합니다. Scoped 로 등록되면 범위 내에 단일 인스턴스가 있습니다. 범위는 무엇이든 될 수 있으며 IServiceProvider.CreateScope() 확장 메서드를 통해 생성됩니다. 예를 들어 ASP.NET에서 모든 HTTP 요청에는 자체 범위가 있습니다. 마지막 유형은 Singleton(GoF 디자인 패턴)이며 이름에서 알 수 있듯이 항상 동일한 인스턴스를 확인합니다.

    요즘 저는 시간 범위가 지정되는 DI 등록을 원했습니다. 따라서 유효한 시간이 만료되면 새 인스턴스를 반환합니다. 유통기한이라고 생각하시면 됩니다.

    참조 시간은 처음 액세스(생성)한 시간이어야 합니다.



    서비스 확인이 작동하는 방식과 새로운 인스턴스가 생성되는 경우에 대한 다이어그램

    용법



    패키지DavidKroell.TimeScoped를 프로젝트에 설치합니다. 패키지는 NuGet.org 에서 구할 수 있습니다.
    AddTimeScoped<TSerice>를 사용하여 DI 컨테이너에 시간 범위 서비스를 추가할 수 있습니다. 그런 다음 시간 범위 서비스를 일반 인터페이스로 확인할 수 있습니다. ITimeScoped<DummyService> 이 인터페이스에는 실제 서비스가 될 단일 속성(Instance)이 있습니다.

    var sc = new ServiceCollection();
    
    sc.AddTimeScoped<DummyService>(TimeSpan.FromSeconds(3));
    
    var provider = sc.BuildServiceProvider();
    
    var timeScopedService = provider.GetRequiredService<ITimeScoped<DummyService>>();
    
    var instance1 = timeScopedService.Instance;
    
    await Task.Delay(5000);
    
    // should return a new instance, because 3 seconds are over
    var instance2 = timeScopedService.Instance;
    
    Assert.AreNotSame(instance1, instance2);
    


    Never store an instance of your real service in an variable!
    You should always access it within the wrapper class.



    구현



    이것의 구현은 매우 간단합니다. 기본 논리는 모두 Instance getter 메서드 내에 있습니다.

    internal class TimeScopedProvider<TService> : ITimeScoped<TService> where TService : class
    {
        private readonly IServiceProvider _provider;
        private readonly TimeSpan _validTimeSpan;
        private DateTime? _validUntil;
        private TService? _instance;
        public TService Instance => GetInstance();
    
        public TimeScopedProvider(IServiceProvider provider, TimeSpan validTimeSpan)
        {
            _provider = provider;
            _validTimeSpan = validTimeSpan;
        }
    
        private TService GetInstance()
        {
            if (_validUntil == null || _validUntil < DateTime.Now)
            {
                lock (this)
                {
                    CleanupOldInstance();
    
                    _instance = (TService) _provider.GetService(typeof(TService))!;
    
                    _validUntil = DateTime.Now.Add(_validTimeSpan);
                }
            }
    
            return _instance!;
        }
    
        private void CleanupOldInstance()
        {
            if (_instance is IDisposable disposable)
            {
                disposable.Dispose();
            }
    
            _instance = null;
        }
    }
    


    요약



    때때로 새 인스턴스를 원할 때 이 구현을 사용할 수 있지만 모든 액세스에 대해서는 아닙니다. 간단한 캐시 구현을 위해 한 번 사용했습니다.

    좋은 웹페이지 즐겨찾기