IOptions<T>, IOptionsSnapshot<T>, IOptionsMonitor<T>

λ‹·λ„·μ˜ Host κ°μ²΄λŠ” DI, μ„€μ •, 그리고, μ•±μ˜ 생애 μ£ΌκΈ° κ΄€λ ¨ 도ꡬλ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.

일반적으둜 "호슀트"라고 ν•˜λ©΄ λ„€νŠΈμ›Œν¬κ°€ μ—°μƒλ˜μ§€λ§Œ, λ‹·λ„·μ—μ„œλŠ” λ„€νŠΈμ›Œν¬λ₯Ό κ²°λΆ€ν•˜μ§€ μ•Šμ€ κ°œλ…μœΌλ‘œ μ‚¬μš©ν•©λ‹ˆλ‹€. λ„€νŠΈμ›Œν¬ κΈ°λŠ₯을 κ²°λΆ€ν•˜μ—¬, WebHost λΌλŠ” νŒŒμƒ 객체도 μ œκ³΅ν•˜κ³ , 이λ₯Ό κΈ°λ°˜ν•œ μ›Ήμ•±μœΌλ‘œ WebApplication(Asp.Net Core μ•±) 도 μ œκ³΅ν•©λ‹ˆλ‹€.

λ‹·λ„·μ—μ„œ "Configuration"의 μ˜λ―ΈλŠ” (μ•±μ˜ μž¬λΉŒλ“œ/μž¬μ‹œμž‘ 없이) μ•±μ˜ νŒŒλΌλ―Έν„°λ₯Ό μ„€μ •/λ³€κ²½ν•˜λŠ” 것을 κ°€λ¦¬ν‚΅λ‹ˆλ‹€. 참고둜 λ‹·λ„· λ¬Έμ„œ, VS μ—μ„œλŠ” "ꡬ성"이라고 λ²ˆμ—­ν•˜λŠ”λ°, 이 κΈ€μ—μ„œλŠ” "μ„€μ •"이라고 ν‘œν˜„ν•©λ‹ˆλ‹€.

λ‹·λ„· Host의 μ„€μ • μ‹œμŠ€ν…œμ€, μ„€μ • κ³΅κΈ‰μž( λŒ€ν‘œμ μœΌλ‘œ JsonFileProvider - appsettings.json 의 컨텐츠λ₯Ό 섀정값에 μΆ”κ°€)κ°€ μ œκ³΅ν•œ 값을 μ½”λ“œμ— κ³΅κΈ‰ν•©λ‹ˆλ‹€.

κ³΅κΈ‰ν•˜λŠ” 방식은 μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.

  • raw 값을 κ·ΈλŒ€λ‘œ (이후 μ½”λ“œμ—μ„œ 객체에 μˆ˜λ™ 바인딩할 수 있음)
  • ConfigurationBinderλ₯Ό 톡해 λ°”μΈλ”©λœ 객체

이 글은 두 번째 방식과 μ—°κ΄€λœ, μ œν•˜μ˜ μΈν„°νŽ˜μ΄μŠ€ νŒ¨λ°€λ¦¬μ— κ΄€ν•œ κ²ƒμž…λ‹ˆλ‹€.

예제λ₯Ό μœ„ν•΄,

μ„€μ • μ†ŒμŠ€

appsettins.json

{
   "Upstreams" :  {
      "Backends" : [ "https://app1.mysite.com", "https://app2.mysite.com" ]
   }
}

바인딩 λͺ¨λΈ

public sealed class Upstreams // κΈ°λ³Έ μƒμ„±μž μžˆμ–΄μ•Ό 함.
{
    public Uri[] Backends { get; set; } = []; // get, set λͺ¨λ‘ μžˆμ–΄μ•Ό 함.
}

μ„€μ • 바인딩을 μœ„ν•œ λͺ¨λΈμ€ μ•„λž˜μ˜ μš”κ΅¬ 사항을 λ§Œμ‘±ν•΄μ•Ό ν•©λ‹ˆλ‹€.

  • κΈ°λ³Έμƒμ„±μž 쑴재
  • public read/write 속성

μžλ™ 바인딩 μ„€μ •

// program.cs
builder.Configure<Upstreams>(builder.Configuration.GetSection("Upstreams"));

μ°Έκ³ 
Upstreams.Backends λŠ” Uri[] νƒ€μž…μ΄κ³ , appsettings.Upstreams.Backends의 ν˜•μ‹μ€ string[] νƒ€μž…λ‹ˆλ‹€. μ„€μ • 바이딩 μ‹œμ—, string 이 Uri 에 μžλ™ λ³€ν™˜λ˜λŠ” 편리 κΈ°λŠ₯이 μžˆμŠ΅λ‹ˆλ‹€.

μžλ™ 바인딩 μ„€μ •μœΌλ‘œ 인해, 우리 μ½”λ“œλŠ” Upstreams 객체λ₯Ό μ•„λž˜ μ„Έ κ°€μ§€ νƒ€μž… 쀑 ν•˜λ‚˜λ‘œ μ„ νƒν•΄μ„œ 받을 수 μžˆμŠ΅λ‹ˆλ‹€.

  • IOptions<Upstreams>
  • IOptionsSnapshot<Upstreams>
  • IOptionsMonitor<Upstreams>

IOptions<T>

Value 속성을 처음으둜 읽을 λ•Œ 값이 ν™•μ •λ˜κ³ , 이 값은 μ•±μ˜ μ’…λ£ŒκΉŒμ§€ λ³€ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

class ScopedService(IOptions<Upstreams> upstreamsOptions)
{
   public void Propagate()
   {
       var uptreams = upstreamsOptions.Value; // 읽기
       // ...

주석에 β€œμ½κΈ°β€ 라고 ν‘œμ‹œλœ μ½”λ“œκ°€ 처음으둜 싀행될 λ•Œ(Lazy), IOptionsκ°€ μƒμ„±λ˜κ³ , 이후 appsettigns.json 을 톡해 값을 λ³€κ²½ν•˜λ”λΌλ„ μž¬μƒμ„±λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

μ΄λŠ” 마치 싱글톀 μ„œλΉ„μŠ€μ™€ μœ μ‚¬ν•˜κ³ , μ‹€μ œλ‘œλ„ μ£Όμž… λ°›λŠ”λ° μ œν•œμ΄ μ—†μŠ΅λ‹ˆλ‹€.

IOptionsSnapshot<T>

λ™μΌν•˜κ²Œ Value 속성을 μ œκ³΅ν•˜μ§€λ§Œ, 객체의 수λͺ…μ£ΌκΈ°λŠ” Scope μ’…μ†μ μž…λ‹ˆλ‹€.
즉, Scope λ§ˆλ‹€ μƒμ„±λ˜κ³ , 생성될 λ•Œ 값을 μ„€μ • 값을 λ°˜μ˜ν•©λ‹ˆλ‹€.

μŠ€μ½”ν”„ λ§ˆλ‹€ λ°˜λ“œμ‹œ 확인해야 ν•˜λŠ” 값이라면, 이 μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.

Scoped μ„œλΉ„μŠ€μ™€ λ§ˆμ°¬κ°€μ§€λ‘œ, μŠ€μ½”ν”„ μ»¨ν…μŠ€νŠΈ μ—†λŠ” μ„œλΉ„μŠ€ κ²°μ •(Service resolution) μ½”λ“œλŠ” λŸ°νƒ€μž„ μ—λŸ¬λΌλŠ” 점에 μ£Όμ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€.

// λŸ°νƒ€μž„ μ—λŸ¬
 var upstreamsOptions = sp.GetRequiredService<IOptionsSnapshot<UpStreams>>();

λ°˜λ“œμ‹œ μŠ€μ½”ν”„ λ‚΄λΆ€μ—μ„œ 생성해야 ν•©λ‹ˆλ‹€.

using var scope = serviceProvider.CreateScope();
var upstreamsOptions = scope.ServiceProvider
   .GetRequiredService<IOptionsSnapshot<UpStreams>>();
var upstreams = upstreamsOptions.Value;

IOptionsMonitor<T>

이 μΈν„°νŽ˜μ΄μŠ€λŠ” λ‹€λ₯Έ ν˜•μ œλ“€κ³Ό λ‹€λ₯΄κ²Œ CurrnetValue 속성을 μ œκ³΅ν•˜λŠ”λ°, 이 μ†μ„±μ˜ 값이 μ„€μ • κ°’(appsettings.json)κ³Ό λ¦¬μ–Όνƒ€μž„μœΌλ‘œ μΌμΉ˜ν•¨μ„ 보μž₯ν•©λ‹ˆλ‹€.

μ„€μ • 파일의 λ³€κ²½ 사항을 μ‹€μ‹œκ°„μœΌλ‘œ μ•Œκ³  싢을 λ•Œ 이 객체λ₯Ό μ‚¬μš©ν•˜λ©΄ λ˜λŠ”λ°, κ·Έ μ˜ˆμ€‘μ— ν•˜λ‚˜κ°€ 예제둜 쓰인 Upstreams 객체일 κ²ƒμž…λ‹ˆλ‹€.

life-time 은 μ‹±κΈ€ν„΄μœΌλ‘œ 생성/μ£Όμž… μ‹œ νŠΉλ³„ν•œ μ£Όμ˜κ°€ ν•„μš”μΉ˜ μ•ŠμŠ΅λ‹ˆλ‹€.

λ˜ν•œ OnChange λΌλŠ” 이벀트λ₯Ό μ œκ³΅ν•˜λŠ”λ°, μ„€μ • 값에 변경이 μžˆμ„ λ•Œ λ°œν–‰λ˜μ–΄ 데이터 바인딩 λ“±, λ°œν–‰μž-κ΅¬λ…μž νŒ¨ν„΄μ˜ μ½”λ“œμ— μœ μš©ν•©λ‹ˆλ‹€.

μΈν„°νŽ˜μ΄μŠ€ 별 μ„±λŠ₯

Value λ‚˜ CurrentValue λ₯Ό 읽을 λ•ŒλŠ” νŠΉλ³„ν•œ μ„±λŠ₯ μ˜€λ²„ν—€λ“œλŠ” μ—†λ‹€κ³  ν•©λ‹ˆλ‹€.

λ‹€λ§Œ, IOptionsMonitor<T>.OnChange 에 λ§Žμ€ κ΅¬λ…μžκ°€ λ“±λ‘λœ κ²½μš°μ—λŠ” ν΄λΌμ΄μ–ΈνŠΈ μ½”λ“œ μ„±λŠ₯에 영ν–₯을 쀄 수 μžˆμ–΄, 무거운 이벀트 ν•Έλ“€λŸ¬λŠ” 가급적 비동기 νŒ¨ν„΄μœΌλ‘œ κ΅¬ν˜„ν•˜λŠ” 것이 κΆŒκ³ λ©λ‹ˆλ‹€.

options.OnChanged += async (value, name) => { await Task.Run(() => { }, value); };
7개의 μ’‹μ•„μš”