เหตุใด HttpContext.Current จึงเป็นโมฆะหลังจากรอ


91

ฉันมีรหัสทดสอบ WebAPI ต่อไปนี้ฉันไม่ได้ใช้ WebAPI ในการผลิต แต่ฉันทำสิ่งนี้เพราะการสนทนาที่ฉันมีเกี่ยวกับคำถามนี้: คำถามWebAPI Async

อย่างไรก็ตามนี่คือวิธีการ WebAPI ที่ไม่เหมาะสม:

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";
}

ฉันเชื่อในที่นี้ว่าข้อยกเว้นที่สองคาดว่าจะเกิดขึ้นเนื่องจากเมื่อการawaitดำเนินการเสร็จสมบูรณ์มีแนวโน้มว่าจะอยู่ในเธรดอื่นโดยที่HttpContext.Currentตัวแปรคงเธรดจะไม่สามารถแก้ไขเป็นค่าที่เหมาะสมได้อีกต่อไป ตอนนี้ตามบริบทการซิงโครไนซ์อาจถูกบังคับให้กลับไปที่เธรดเดิมหลังจากรอ แต่ฉันไม่ได้ทำอะไรที่น่าสนใจในการทดสอบของฉัน นี่เป็นเพียงการใช้งานแบบไร้เดียงสาธรรมดาawait

ในความคิดเห็นในคำถามอื่นฉันได้รับคำสั่งว่าHttpContext.Currentควรแก้ไขหลังจากรอ ยังมีความคิดเห็นอื่น ๆ ในคำถามนี้ที่ระบุว่าเหมือนกัน แล้วอะไรจริง? ควรแก้ไขหรือไม่? ฉันคิดว่าไม่ แต่ฉันต้องการคำตอบที่เชื่อถือได้สำหรับเรื่องนี้เพราะasyncและawaitใหม่พอที่ฉันไม่สามารถหาข้อสรุปที่ชัดเจนได้

TL; DR: HttpContext.Currentอาจnullเป็นหลังจากawait?


3
คำถามของคุณไม่ชัดเจน - คุณได้พูดในสิ่งที่คุณคาดไว้ว่าจะเกิดขึ้นและความคิดเห็นระบุว่านั่นคือสิ่งที่เกิดขึ้น ... แล้วคุณจะสับสนอะไร?
Jon Skeet

@ user2674389 นี้ทำให้เข้าใจผิด มันAspNetSynchronizationContextที่ดูแลไม่ได้HttpContext awaitยิ่งไปกว่านั้นการเรียกกลับต่อเนื่องสำหรับawaitพฤษภาคม (และส่วนใหญ่จะ) เกิดขึ้นในเธรดอื่นสำหรับรูปแบบการเรียกใช้ Web API
อัตราส่วนจมูก

แก้ไขเพื่อตั้งคำถามที่รวบรัด
welegan

1
@JoepBeusenberg การสร้างแอสเซมบลีแยกต่างหากที่ใช้งานได้เฉพาะเมื่อถูกเรียกจากแอสเซมบลีที่ดำเนินการภายในบริบทของคำขอ HTTP ของเว็บสแต็กโดยเฉพาะดูเหมือนว่าอาจทำให้การทดสอบการบำรุงรักษาและการใช้ซ้ำเป็นความท้าทาย
Darrel Miller

1
@DarrelMiller ค่อนข้างตรงกันข้าม ฉันได้แยกตรรกะทางธุรกิจออกจากโครงการเว็บจริง การใช้การฉีดขึ้นต่อกันฉันสามารถเพิ่มไลบรารี webapi-awareness ที่อยู่ด้านบนของตรรกะทางธุรกิจ แต่ไลบรารีนี้หยุดทำงานเมื่อตรรกะทางธุรกิจได้ทำ.ConfigureAwait(false)บางส่วนลงไป ไม่มีการส่งคำขอหรือตัวควบคุมอย่างชัดเจนผ่านชั้นธุรกิจเนื่องจากคำขอนั้นไม่ได้รับการยอมรับทางเว็บ TraceInformationนี้จะเป็นประโยชน์สำหรับตัวอย่างสำหรับโมดูลการเข้าสู่ระบบที่สามารถฉีดขอรายละเอียดเมื่อตรรกะทางธุรกิจเขียนทั่วไป
Joep Beusenberg

คำตอบ:


150

โปรดตรวจสอบว่าคุณกำลังเขียนแอปพลิเคชันASP.NET 4.5และกำหนดเป้าหมาย 4.5 asyncและawaitมีพฤติกรรมที่ไม่ได้กำหนดไว้บน ASP.NET เว้นแต่ว่าคุณกำลังรันบน 4.5 และกำลังใช้บริบทการซิงโครไนซ์ "ที่ใช้งานง่าย"

โดยเฉพาะอย่างยิ่งนี่หมายความว่าคุณต้อง:

  • ตั้งค่าhttpRuntime.targetFrameworkเป็น4.5หรือ
  • ในของคุณappSettingsตั้งไปaspnet:UseTaskFriendlySynchronizationContexttrue

ดูข้อมูลเพิ่มเติมได้ที่นี่


2
ฉันเพิ่งสร้างโครงการ ASP.NET 4.5 WebAPI ใหม่คัดลอก / วางโค้ดของคุณและทำการทดสอบ มันทำงานได้อย่างสมบูรณ์แบบสำหรับฉัน (ไม่มีข้อยกเว้นถูกโยนทิ้ง) โปรดตรวจสอบอีกครั้งว่าคุณกำลังใช้งานและกำหนดเป้าหมาย 4.5
Stephen Cleary

3
ฉันมีกรอบเป้าหมาย: .NET Framework 4.5 ตั้งไว้ ฉันไม่รู้จะบอกอะไรคุณมันเป็นโมฆะในเครื่องของฉัน
welegan

24
<httpRuntime targetFramework="4.5" />คือสิ่งที่แก้ไขได้ขอบคุณสำหรับคำชี้แจง
welegan

1
@Vince: 4.5.1 ควรใช้งานได้ดี ฉันไม่แน่ใจว่าคุณควร / สามารถตั้งค่าtargetFrameworkเป็น 4.5.1 หรือ 4.5 ได้ แต่ async บน 4.5.1 ควรทำงานได้ดี
Stephen Cleary

1
จะเกิดอะไรขึ้นถ้าคุณเขียนตัวจัดการที่มีการจัดการของคุณเอง ฉันยังคงใช้ HttpContext.Current = null ในนั้นแม้ว่าจะเพิ่มรายการเหล่านี้ใน web.config แล้วก็ตาม
Brain2000

30

ตามที่ @StephenCleary ระบุไว้อย่างถูกต้องคุณต้องมีสิ่งนี้ใน web.config ของคุณ:

<httpRuntime targetFramework="4.5" />

เมื่อฉันแก้ไขปัญหานี้ครั้งแรกฉันทำการค้นหาทั้งโซลูชันสำหรับข้างต้นยืนยันว่ามีอยู่ในโปรเจ็กต์เว็บทั้งหมดของฉันและยกเลิกอย่างรวดเร็วว่าเป็นผู้ร้าย ในที่สุดฉันก็ต้องดูผลการค้นหาในบริบททั้งหมด:

<!--
  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.

บทเรียน: หากคุณอัปเกรดโปรเจ็กต์เว็บเป็น 4.5 คุณยังต้องรับการตั้งค่านั้นด้วยตนเอง


22
gotcha อื่นคือสิ่งนี้แตกต่างจาก <compilation targetFramework "4.5" />
Andrew

3

การทดสอบของฉันมีข้อบกพร่องหรือมีองค์ประกอบ web.config บางส่วนที่ฉันขาดหายไปที่นี่ซึ่งจะทำให้ HttpContext การแก้ไขปัจจุบันถูกต้องหลังจากรอ

การทดสอบของคุณไม่มีข้อบกพร่องและ HttpContext.Current ไม่ควรเป็นโมฆะหลังจากการรอคอยเนื่องจากใน ASP.NET Web API เมื่อคุณรอสิ่งนี้จะช่วยให้มั่นใจได้ว่าโค้ดที่ตามหลังนี้จะผ่าน HttpContext ที่ถูกต้องที่มีอยู่ก่อนรอ


คุณแน่ใจเกี่ยวกับเธรดความต่อเนื่องเดียวกันสำหรับ WebAPI หรือไม่ ฉันจัดการกับกรณีที่มันเป็นหัวข้ออื่น
noseratio

4
ASP.NET จะดำเนินการต่อบนเธรดพูลเธรดใด ๆ แต่มีบริบทการร้องขอที่ถูกต้อง
Stephen Cleary

2
ใช่คุณพูดถูกเธรดอาจไม่เหมือนกัน แต่ HttpContext.Current จะเหมือนกับก่อนที่จะรอ ฉันได้อัปเดตคำถามของฉันแล้ว
Darin Dimitrov

4
HttpContext.Current เป็นโมฆะหลังจากรอในรหัสของฉันและฉันกำหนดเป้าหมาย. net 4.6.1
Triynko

1
สำหรับฉัน HttpContext ปัจจุบันเป็นโมฆะก่อนฟังก์ชัน await
JobaDiniz

3

ฉันพบปัญหานี้เมื่อเร็ว ๆ นี้ ดังที่ Stephen ชี้ให้เห็นว่าการไม่กำหนดกรอบเป้าหมายอย่างชัดเจนสามารถสร้างปัญหานี้ได้

ในกรณีของฉัน Web API ของเราถูกย้ายไปยังเวอร์ชัน 4.6.2 แต่ไม่เคยระบุเฟรมเวิร์กเป้าหมายรันไทม์ในการกำหนดค่าเว็บดังนั้นโดยพื้นฐานแล้วสิ่งนี้จะหายไปในแท็ก <system.web>:

หากคุณมีข้อสงสัยเกี่ยวกับเวอร์ชันของเฟรมเวิร์กที่คุณกำลังเรียกใช้สิ่งนี้อาจช่วยได้: เพิ่มบรรทัดต่อไปนี้ในเมธอด Web API ของคุณและตั้งค่าเบรกพอยต์เพื่อตรวจสอบประเภทที่โหลดอยู่ในขณะรันไทม์และตรวจสอบว่าไม่ใช่การใช้งานแบบเดิม

คุณควรเห็นสิ่งนี้ (AspNetSynchronizationContext):

ป้อนคำอธิบายภาพที่นี่

แทนที่จะเป็น LegazyAspNetSynchronizationContext (ซึ่งเป็นสิ่งที่ฉันเห็นก่อนเพิ่มกรอบเป้าหมาย):

ป้อนคำอธิบายภาพที่นี่

หากคุณไปที่ซอร์สโค้ด ( https://referencesource.microsoft.com/#system.web/LegacyAspNetSynchronizationContext.cs ) คุณจะเห็นว่าการใช้งานอินเทอร์เฟซนี้แบบเดิมขาดการสนับสนุนแบบอะซิงโครนัส

ป้อนคำอธิบายภาพที่นี่

ฉันใช้เวลาพยายามหาต้นตอของปัญหาอยู่นานและคำตอบของ Stephen ก็ช่วยได้มาก หวังว่าคำตอบนี้จะให้ข้อมูลเพิ่มเติมเกี่ยวกับปัญหานี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.