อินสแตนซ์ของคลาสแบบคงที่ไม่ซ้ำกับคำขอหรือเซิร์ฟเวอร์ใน ASP.NET หรือไม่


182

บนเว็บไซต์ ASP.NET มีคลาสสแตติกที่ไม่ซ้ำกันสำหรับแต่ละคำขอของเว็บหรือพวกมันจะถูกสร้างอินสแตนซ์เมื่อใดก็ตามที่จำเป็นและ GCed เมื่อใดก็ตามที่ GC ตัดสินใจที่จะกำจัดพวกเขา?

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

หากพวกเขาไม่ซ้ำกันในแต่ละคำขอมีวิธีที่จะอนุญาตให้พวกเขาเป็น?

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

using System.Collections;
using System.Web;

public class GloballyAccessibleClass
{
    private GloballyAccessibleClass() { }

    public static GloballyAccessibleClass Instance
    {
        get
        {
            IDictionary items = HttpContext.Current.Items;
            if(!items.Contains("TheInstance"))
            {
                items["TheInstance"] = new GloballyAccessibleClass();
            }
            return items["TheInstance"] as GloballyAccessibleClass;
        }
    }
}

หัวขึ้น: หากคุณเปลี่ยนเส้นทางคำขอของคุณพูดกับfilterContext.Result = new RedirectResult(...)คุณจะสูญเสียรายการของคุณเพราะ HttpContext ใหม่จะถูกสร้างขึ้น รายละเอียดเพิ่มเติมได้ที่นี่: stackoverflow.com/questions/16697601/…
Reuel Ribeiro

คำถามที่เกี่ยวข้องกับคำตอบที่ดีที่stackoverflow.com/q/5219431
Theophilus

คำตอบ:


146

คลาสสแตติกและฟิลด์สแตติกอินสแตนซ์ของคุณจะถูกแชร์ระหว่างคำขอทั้งหมดไปยังแอปพลิเคชันและมีอายุการใช้งานเท่ากับโดเมนแอปพลิเคชัน ดังนั้นคุณควรระมัดระวังเมื่อใช้อินสแตนซ์แบบคงที่เนื่องจากคุณอาจมีปัญหาการซิงโครไนซ์และสิ่งที่คล้ายกัน โปรดจำไว้ว่าอินสแตนซ์แบบคงที่จะไม่ถูก GC'ed ก่อนที่กลุ่มแอปพลิเคชันจะถูกรีไซเคิลดังนั้นทุกสิ่งที่ถูกอ้างอิงโดยอินสแตนซ์แบบคงที่จะไม่ถูก GC'ed สิ่งนี้สามารถนำไปสู่ปัญหาการใช้งานหน่วยความจำ

หากคุณต้องการอินสแตนซ์ที่มีอายุการใช้งานเท่ากับคำขอฉันขอแนะนำให้ใช้HttpContext.Current.Itemsคอลเลกชัน นี่คือการออกแบบโดยมีวัตถุประสงค์เพื่อเป็นสถานที่ในการจัดเก็บสิ่งที่คุณต้องการผ่านการร้องขอ สำหรับการออกแบบและการอ่านที่ดีขึ้นคุณสามารถใช้รูปแบบ Singleton เพื่อช่วยคุณจัดการรายการเหล่านี้ HttpContext.Current.Itemsเพียงแค่สร้างระดับซิงเกิลที่เก็บตัวอย่างใน (ในไลบรารีทั่วไปของฉันสำหรับ ASP.NET ฉันมีคลาส SingletonRequest สามัญสำหรับวัตถุประสงค์นี้)


3
คุณสามารถให้ตัวอย่างรูปแบบซิงเกิลที่เกี่ยวข้องกับHttpContext.Current.Itemsคุณได้หรือไม่?
Airn5475

รายละเอียดเพิ่มเติมเกี่ยวกับสถานการณ์ของฉัน: ในเว็บแอปของฉันผู้ใช้จะเรียกใช้ผ่านไลบรารีคลาสของรหัสที่ใช้คุณสมบัติคลาสที่ใช้ร่วมกัน ฉันต้องการให้คุณสมบัตินี้เป็นแบบเจาะจงผู้ใช้ แต่ฉันไม่ต้องการให้คุณสมบัตินี้ส่งไปยังฟังก์ชันอื่น การออกแบบที่คุณพูดถึงสามารถจัดการกับสิ่งนี้ได้อย่างถูกต้องหรือไม่?
Airn5475

ได้โปรดแบ่งปันชั้นเรียน SingletonRequest ได้ไหม?
Tebo

ฉันไม่เก็บข้อมูลใด ๆ ในคลาสที่คงที่ ฉันใช้คลาสสแตติกเพื่อรับข้อมูลหรือตั้งค่าข้อมูลเป็นชั้นข้อมูลเท่านั้น ดังนั้นมีปัญหาอะไรบ้าง?
garish

"static instances will not be GC'ed before the application pool is recycled, and therefore everything that is referenced by the static instance, will not be GC'ed"- มีแหล่งที่มาสำหรับสิ่งนี้เพราะมันไม่สมเหตุสมผลหรือขัดแย้งกับสิ่งที่ฉันอ่านที่อื่น เมื่อ AppPool ถูกรีไซเคิลแล้ว App Domain ที่เกี่ยวข้องจะถูกฉีกขาดและ GC'd เมื่อเกิดเหตุการณ์นี้อินสแตนซ์แบบคงที่ใด ๆ ที่เกี่ยวข้องจะเป็น GC'd เนื่องจากรูตของพวกเขา (AppDomain) หายไป ในฐานะที่เป็นส่วนหนึ่งของการรีไซเคิลพูล AppDomain ใหม่จะถูกสร้างขึ้น
Nick

30

สมาชิกแบบสแตติกมีขอบเขตของกระบวนการผู้ปฏิบัติงานปัจจุบันเท่านั้นดังนั้นจึงไม่เกี่ยวข้องกับการร้องขอเนื่องจากการร้องขอที่แตกต่างกันอาจหรืออาจไม่ได้รับการจัดการโดยกระบวนการของผู้ปฏิบัติงานเดียวกัน

  • เพื่อแบ่งปันข้อมูลกับผู้ใช้เฉพาะและคำขอข้ามให้ใช้ HttpContext.Current.Session
  • เพื่อแบ่งปันข้อมูลภายในคำขอเฉพาะให้ใช้ HttpContext.Current.Items
  • เพื่อแบ่งปันข้อมูลข้ามแอปพลิเคชันทั้งหมดให้เขียนกลไกสำหรับการนั้นหรือกำหนดค่า IIS ให้ทำงานกับกระบวนการเดียวและเขียนแอปพลิเคชันแบบซิงเกิล / ใช้

โดยวิธีการจำนวนเริ่มต้นของกระบวนการของผู้ปฏิบัติงานคือ 1 ดังนั้นนี่คือเหตุผลว่าทำไมเว็บจึงเต็มไปด้วยผู้คนที่คิดว่าสมาชิกแบบสแตติกมีขอบเขตของแอปพลิเคชันทั้งหมด


11

เนื่องจากประเภทนั้นมีอยู่ในโดเมนแอพฉันจึงคาดว่าจะมีคลาสแบบสแตติกปรากฏตราบใดที่โดเมนแอพไม่ได้ถูกนำมารีไซเคิลหรือหากคำขอได้รับการบริการจากโดเมนแอปอื่น

ฉันคิดว่าหลายวิธีที่จะทำให้วัตถุเฉพาะกับคำขอเฉพาะนั้นขึ้นอยู่กับสิ่งที่คุณต้องการทำเช่นคุณสามารถยกตัวอย่างวัตถุใน Application.BeginRequest แล้วเก็บไว้ในวัตถุ HttpRequest เพื่อให้วัตถุทั้งหมดใน ขั้นตอนการประมวลผลคำขอ


4

หากพวกเขาไม่ซ้ำกันในแต่ละคำขอมีวิธีที่จะอนุญาตให้พวกเขาเป็น?

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


1

โดยทั่วไปวิธีการคงที่คุณสมบัติและชั้นเรียนเป็นเรื่องธรรมดาที่ Applicationระดับ ตราบใดที่แอปพลิเคชันยังมีชีวิตอยู่พวกเขาจะแบ่งปัน

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

คุณสามารถใช้ได้ HttpContext.Current.Itemsเพื่อจัดทำรายการสำหรับคำขอเดียวหรือHttpContext.Current.Sessionเพื่อจัดทำรายการสำหรับผู้ใช้หนึ่งคน (ข้ามคำขอ)

แม้ว่าโดยทั่วไปแล้วเว้นแต่ว่าคุณจะต้องใช้สิ่งต่าง ๆ เช่นServer.Transferวิธีที่ดีที่สุดคือการสร้างสิ่งต่าง ๆ เพียงครั้งเดียวแล้วส่งผ่านอย่างชัดเจนผ่านวิธีการเรียกใช้


3
Jon Skeet แสดงให้เราเห็นว่า ThreadStatic ไม่เคยปลอดภัยใน ASP.Net stackoverflow.com/questions/4791208/ …
Mark Lindell

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