İptal Jetonu Enjeksiyonu

Sep 30 2020

İptal belirteçlerini her seferinde parametre olarak kullanmak yerine bağımlılık ekleme yoluyla iletebilmek istiyorum. Bu bir şey mi?

Hizmet verdiğimiz fintech düzenleyici etki alanının bizans gereksinimlerini karşılamak için denetleyicilerden gelen çağrıları zaman uyumsuz kitaplıklar, işleyiciler ve diğer hizmetlerden oluşan bir labirente ilettiğimiz bir asp.net-core 2.1 uygulamamız var.

İsteğin en üstünde, bir iptal jetonu istediğimi beyan edebilirim ve bir tane alacağım:

    [HttpPost]
    public async Task<IActionResult> DoSomeComplexThingAsync(object thing, CancellationToken cancellationToken) {
        await _someComplexLibrary.DoThisComplexThingAsync(thing, cancellationToken);
        return Ok();
    }

Şimdi, iyi bir zaman uyumsuz programcı olmak cancellationTokenve çağrı zinciri boyunca her zaman uyumsuz yönteme geçildiğinden emin olmak istiyorum . EF'ye, System.IO akışlarına, vb. Aktarıldığından emin olmak istiyorum. Beklediğiniz tüm olağan depo kalıplarına ve ileti gönderme uygulamalarına sahibiz. Yöntemlerimizi kısa ve öz tutmaya çalışıyoruz ve tek bir sorumluluğumuz var. Teknoloji liderim, 'Fowler' kelimesiyle gözle görülür şekilde uyarılıyor. Yani sınıf büyüklüklerimiz ve işlev bedenlerimiz küçük ama çağrı zincirlerimiz çok çok derin.

Bunun anlamı şudur: Her katmanın, her işlevin lanet olası jetonu teslim etmesi gerekir:

    private readonly ISomething _something;
    private readonly IRepository<WeirdType> _repository;

    public SomeMessageHandler(ISomething<SomethingElse> something, IRepository<WeirdType> repository) {
        _something = something;
        _repository = repository;
    }

    public async Task<SomethingResult> Handle(ComplexThing request, CancellationToken cancellationToken) {
        var result = await DoMyPart(cancellationToken);
        cancellationToken.ThrowIfCancellationRequested();
        result.SomethingResult = await _something.DoSomethingElse(result, cancellationToken);
        return result;
    }

    public async Task<SomethingResult> DoMyPart(ComplexSubThing request, CancellationToken cancellationToken) {
        return await _repository.SomeEntityFrameworkThingEventually(request, cancellationToken);
    }

Bu, etki alanı karmaşıklığımızın ihtiyaçlarına göre sonsuza kadar devam eder. Görünüşe CancellationTokengöre kod tabanımızda diğer herhangi bir terimden daha fazla görünüyor . Bir milyon nesne türü bildirmemize rağmen argüman listelerimiz genellikle zaten çok uzundur (yani birden fazla). Ve şimdi, her argüman listesinde, her metodda, fazladan küçük bir iptal belirteci arkadaşımız var.

Sorum şu ki, Kestrel ve / veya boru hattı bana jetonu en başta verdiğinden, böyle bir şeye sahip olsaydım harika olurdu:

    private readonly ISomething _something;
    private readonly IRepository<WeirdType> _repository;
    private readonly ICancellationToken _cancellationToken;

    public SomeMessageHandler(ISomething<SomethingElse> something, ICancellationToken cancellationToken) {
        _something = something;
        _repository = repository;
        _cancellationToken = cancellationToken;
    }

    public async Task<SomethingResult> Handle(ComplexThing request) {
        var result = await DoMyPart(request);
        _cancellationToken.ThrowIfCancellationRequested();
        result.SomethingResult = await _something.DoSomethingElse(result);
        return result;
    }

    public async Task<SomethingResult> DoMyPart(ComplexSubThing request) {
        return await _repository.SomeEntityFrameworkThingEventually(request);
    }

Bu daha sonra DI kompozisyonu aracılığıyla geçecek ve jetona açıkça ihtiyaç duyan bir şeye sahip olduğumda bunu yapabilirdim:

    private readonly IDatabaseContext _context;
    private readonly ICancellationToken _cancellationToken;

    public IDatabaseRepository(IDatabaseContext context, ICancellationToken cancellationToken) {
        _context = context;
        _cancellationToken = cancellationToken;
    }

    public async Task<SomethingResult> DoDatabaseThing() {
        return await _context.EntityFrameworkThing(_cancellationToken);
    }

Ben deli miyim? Lanet jetonu her lanet seferinde uzatıyor muyum ve verilen ödül için asenkron tanrıları övüyor muyum? Bir lama çiftçisi olarak yeniden eğitim mi almalıyım? Hoş görünüyorlar. Bunu sormak bile bir tür sapkınlık mı? Şimdi tövbe mi etmeliyim? Async / await'in düzgün çalışması için token'ın func decl'de olması gerektiğini düşünüyorum. Yani, belki de lamas

Yanıtlar

1 Keith Oct 06 2020 at 04:19

Her şeyden önce, 3 enjeksiyon kapsamı vardır: Singleton, Scoped ve Transient. Bunlardan ikisi, paylaşılan bir belirteç kullanmayı ekarte ediyor.

Eklenen DI hizmetleri AddSingletontüm isteklerde mevcuttur, bu nedenle herhangi bir iptal belirteci belirli bir yönteme (veya uygulamanızın tamamına) aktarılmalıdır.

Eklenen DI hizmetleri AddTransienttalep üzerine başlatılabilir ve zaten iptal edilmiş olan bir belirteç için yeni bir eşgörünüm oluşturulduğunda sorunlar yaşayabilirsiniz. Muhtemelen mevcut jetonun aktarılması [FromServices]veya başka bir kütüphane değişikliği için bir yola ihtiyaçları olacaktır .

Ancak, AddScopedbence bir yolu var ve benzer soruma bu cevabım bana yardımcı oldu - jetonun kendisini DI'ye geçiremezsiniz, ancak geçebilirsiniz IHttpContextAccessor.

Öyleyse, Startup.ConfigureServiceshangi IRepositoryamaçla kullanılırsa kullanılsın kaydetmek için kullandığınız uzantı yöntemi :


// For imaginary repository that looks something like
class RepositoryImplementation : IRepository {
    public RepositoryImplementation(string connection, CancellationToken cancellationToken) { }
}

// Add a scoped service that references IHttpContextAccessor on create
services.AddScoped<IRepository>(provider => 
    new RepositoryImplementation(
        "Repository connection string/options",
        provider.GetService<IHttpContextAccessor>()?.HttpContext?.RequestAborted ?? default))

Bu IHttpContextAccessorhizmet, HTTP isteği başına bir kez ?.HttpContext?.RequestAbortedalınacaktır ve bu , bir denetleyici eyleminin içinden CancellationTokençağırmış this.HttpContext.RequestAbortedveya eylemdeki parametrelere eklemişsiniz gibi aynı şeyi döndürür .