Mengapa HttpContext.Current null setelah menunggu?

90

Saya memiliki kode WebAPI pengujian berikut, saya tidak menggunakan WebAPI dalam produksi, tetapi saya membuatnya karena diskusi yang saya lakukan tentang pertanyaan ini: Pertanyaan Asinkron WebAPI

Bagaimanapun, inilah metode WebAPI yang melanggar:

public async Task<string> Get(int id)
{
    var x = HttpContext.Current;
    if (x == null)
    {
        // not thrown
        throw new ArgumentException("HttpContext.Current is null");
    }

    await Task.Run(() => { Task.Delay(500); id = 3; });

    x = HttpContext.Current;
    if (x == null)
    {
        // thrown
        throw new ArgumentException("HttpContext.Current is null");
    }

    return "value";
}

Saya dengan ini percaya bahwa pengecualian kedua diharapkan karena ketika awaitselesai, kemungkinan akan berada di utas yang berbeda dimana HttpContext.Currentvariabel utas-statis tidak akan lagi menyelesaikan ke nilai yang sesuai. Sekarang, berdasarkan konteks sinkronisasi, itu sebenarnya bisa dipaksa untuk kembali ke utas yang sama setelah menunggu tetapi saya tidak melakukan sesuatu yang mewah dalam pengujian saya. Ini hanyalah penggunaan yang polos dan naif await.

Dalam komentar di pertanyaan lain, saya diberi tahu yang HttpContext.Currentharus diselesaikan setelah menunggu. Bahkan ada komentar lain atas pertanyaan ini yang menunjukkan hal yang sama. Jadi apa yang benar? Haruskah itu diselesaikan? Saya pikir tidak, tetapi saya ingin jawaban yang berwibawa tentang ini karena asyncdan awaitcukup baru sehingga saya tidak dapat menemukan sesuatu yang pasti.

TL; DR: Apakah HttpContext.Currentberpotensi nullsetelah await?

welegan
sumber
3
Pertanyaan Anda tidak jelas - Anda telah mengatakan apa yang Anda harapkan akan terjadi, dan komentar menunjukkan hal itu adalah apa yang terjadi ... jadi apa yang membingungkan Anda?
Jon Skeet
@ user2674389, ini menyesatkan. Itu AspNetSynchronizationContextyang mengurus HttpContext, bukan await. Lebih lanjut, kelanjutan callback untuk awaitmay (dan kemungkinan besar akan) terjadi pada thread yang berbeda untuk model eksekusi API Web.
noseratio
diedit untuk mengajukan pertanyaan ringkas
welegan
1
@JoepBeusenberg Membuat rakitan terpisah yang hanya berfungsi ketika dipanggil dari rakitan yang dijalankan dalam konteks permintaan HTTP dari satu tumpukan web tertentu sepertinya akan membuat pengujian, pemeliharaan, dan penggunaan kembali menjadi tantangan.
Darrel Miller
1
@Darreliller Justru sebaliknya. Saya telah memisahkan logika bisnis dari proyek web yang sebenarnya. Menggunakan injeksi ketergantungan saya dapat menambahkan pustaka webapi-aware di atas logika bisnis. Tetapi pustaka ini rusak ketika logika bisnis telah selesai di .ConfigureAwait(false)suatu tempat. Tidak ada permintaan atau pengontrol yang secara eksplisit diserahkan melalui lapisan bisnis, karena lapisan tersebut tidak sadar web. Ini berguna misalnya untuk modul logging yang dapat memasukkan detail permintaan saat logika bisnis menulis generik TraceInformation.
Joep Beusenberg

Jawaban:

148

Harap pastikan Anda menulis aplikasi ASP.NET 4.5 , dan menargetkan 4.5. asyncdan awaitmemiliki perilaku yang tidak ditentukan di ASP.NET kecuali jika Anda menjalankan pada 4.5 dan menggunakan konteks sinkronisasi "ramah-tugas" yang baru.

Secara khusus, ini berarti Anda harus:

  • Setel httpRuntime.targetFrameworkke 4.5, atau
  • Di Anda appSettings, setel aspnet:UseTaskFriendlySynchronizationContextke true.

Informasi lebih lanjut tersedia di sini .

Stephen Cleary
sumber
2
Saya baru saja membuat proyek ASP.NET 4.5 WebAPI baru, menyalin / menempelkan kode Anda, dan melakukan tes. Ini bekerja dengan sempurna untuk saya (tidak ada pengecualian yang dilemparkan). Harap periksa kembali apakah Anda berjalan pada dan menargetkan 4.5.
Stephen Cleary
3
Saya memiliki kerangka Target: .NET Framework 4,5 set. Saya tidak tahu harus memberi tahu Anda apa, ini pasti null di komputer lokal saya.
welegan
24
itulah <httpRuntime targetFramework="4.5" />yang memecahkannya, terima kasih telah mengklarifikasi.
welegan
1
@Vince: 4.5.1 seharusnya berfungsi dengan baik. Saya tidak yakin apakah Anda harus / dapat mengatur targetFrameworkke 4.5.1 atau 4.5, tetapi async pada 4.5.1 seharusnya berfungsi dengan baik.
Stephen Cleary
1
Bagaimana jika Anda membuat penangan terkelola Anda sendiri? Saya terus menghasilkan HttpContext.Current = null di sana bahkan setelah menambahkan item ini di web.config.
Brain2000
28

Seperti yang ditunjukkan dengan benar oleh @StephenCleary, Anda memerlukan ini di web.config Anda:

<httpRuntime targetFramework="4.5" />

Ketika saya pertama kali memecahkan masalah ini, saya melakukan pencarian luas solusi untuk hal di atas, mengonfirmasi bahwa itu ada di semua proyek web saya dan dengan cepat menolaknya sebagai pelakunya. Akhirnya saya terpikir untuk melihat hasil pencarian tersebut dalam konteks lengkap:

<!--
  For a description of web.config changes for .NET 4.5 see http://go.microsoft.com/fwlink/?LinkId=235367.

  The following attributes can be set on the <httpRuntime> tag.
    <system.Web>
      <httpRuntime targetFramework="4.5" />
    </system.Web>
-->

Doh.

Pelajaran: Jika Anda mengupgrade proyek web ke 4.5, Anda masih perlu mengaktifkan pengaturan itu secara manual.

Todd Menier
sumber
22
Gotcha lain adalah ini berbeda dari <compilation targetFramework "4.5" />
Andrew
3

Apakah pengujian saya cacat, atau adakah beberapa elemen web.config yang saya lewatkan di sini yang akan membuat HttpContext.Current menyelesaikan dengan benar setelah menunggu?

Pengujian Anda tidak cacat dan HttpContext.Current tidak boleh null setelah menunggu karena di ASP.NET Web API saat Anda menunggu, ini akan memastikan bahwa kode yang mengikuti menunggu ini melewati HttpContext yang benar yang ada sebelum menunggu.

Darin Dimitrov
sumber
Apakah Anda yakin tentang rangkaian lanjutan yang sama untuk WebAPI? Saya menangani kasus di mana itu adalah utas yang berbeda.
noseratio
4
ASP.NET akan melanjutkan di setiap utas kumpulan utas, tetapi dengan konteks permintaan yang benar.
Stephen Cleary
2
Ya, Anda benar, utasnya mungkin tidak sama tetapi HttpContext.Current akan sama seperti sebelum menunggu. Saya telah memperbarui pertanyaan saya.
Darin Dimitrov
4
HttpContext.Current adalah null setelah menunggu di kode saya, dan saya menargetkan .net 4.6.1.
Triynko
1
bagi saya HttpContext.Current adalah null sebelum fungsi menunggu
JobaDiniz
2

Saya mengalami masalah ini baru-baru ini. Seperti yang ditunjukkan Stephen, tidak menetapkan kerangka target secara eksplisit dapat menimbulkan masalah ini.

Dalam kasus saya, API Web kami dimigrasi ke versi 4.6.2 tetapi kerangka kerja target waktu proses tidak pernah ditentukan dalam konfigurasi web, jadi pada dasarnya ini hilang di dalam tag <system.web>:

Jika Anda ragu tentang versi framework yang Anda jalankan, ini dapat membantu: Tambahkan baris berikut pada salah satu metode API Web Anda dan setel breakpoint untuk memverifikasi jenis apa yang saat ini dimuat saat runtime dan memverifikasi bahwa itu bukan implementasi Legacy:

Anda harus melihat ini (AspNetSynchronizationContext):

masukkan deskripsi gambar di sini

Alih-alih LegazyAspNetSynchronizationContext (Yang saya lihat sebelum menambahkan kerangka target):

masukkan deskripsi gambar di sini

Jika Anda membuka kode sumber ( https://referencesource.microsoft.com/#system.web/LegacyAspNetSynchronizationContext.cs ), Anda akan melihat bahwa implementasi Legacy dari antarmuka ini kekurangan dukungan asinkron.

masukkan deskripsi gambar di sini

Saya menghabiskan banyak waktu mencoba menemukan sumber masalah dan tanggapan Stephen sangat membantu. Semoga jawaban ini memberikan beberapa informasi lebih lanjut tentang masalah ini.

abarrenechea.dll
sumber