Các thể hiện của lớp tĩnh là duy nhất cho một yêu cầu hoặc một máy chủ trong ASP.NET?


182

Trên một trang web ASP.NET, các lớp tĩnh là duy nhất cho mỗi yêu cầu web, hoặc chúng được khởi tạo bất cứ khi nào cần thiết và được cấp phép bất cứ khi nào GC quyết định loại bỏ chúng?

Lý do tôi hỏi là vì tôi đã viết một số lớp tĩnh trước đó trong C # và hành vi khác với những gì tôi mong đợi. Tôi đã dự kiến ​​các lớp tĩnh là duy nhất cho mỗi yêu cầu, nhưng có vẻ như đó không phải là trường hợp.

Nếu chúng không phải là duy nhất cho mỗi yêu cầu, có cách nào để cho phép chúng được không?

CẬP NHẬT:
Câu trả lời Driis cho tôi là chính xác những gì tôi cần. Tôi đã sử dụng một lớp singleton, tuy nhiên nó đang sử dụng một thể hiện tĩnh và do đó được chia sẻ giữa các yêu cầu ngay cả khi người dùng khác nhau, trong trường hợp này là một điều xấu. Sử dụng HttpContext.Current.Itemsgiải quyết vấn đề của tôi một cách hoàn hảo. Đối với bất kỳ ai vấp phải câu hỏi này trong tương lai, đây là cách triển khai của tôi, được đơn giản hóa và rút ngắn để dễ hiểu mô hình:

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

Chỉ cần ngẩng cao đầu: Nếu bạn chuyển hướng yêu cầu của mình, giả sử, filterContext.Result = new RedirectResult(...)bạn sẽ mất các mục của mình vì một HTTPContext mới sẽ được tạo. Thêm chi tiết tại đây: stackoverflow.com/questions/16697601/
Kẻ

Một câu hỏi liên quan với một câu trả lời hay là tại stackoverflow.com/q/5219431 .
Theophilus

Câu trả lời:


146

Các lớp tĩnh và các trường đối tượng tĩnh của bạn được chia sẻ giữa tất cả các yêu cầu đến ứng dụng và có cùng thời gian tồn tại với miền ứng dụng. Do đó, bạn nên cẩn thận khi sử dụng các trường hợp tĩnh, vì bạn có thể gặp sự cố đồng bộ hóa và tương tự. Cũng lưu ý rằng, các thể hiện tĩnh sẽ không được xử lý trước khi nhóm ứng dụng được tái chế và do đó, mọi thứ được tham chiếu bởi thể hiện tĩnh, sẽ không được xử lý. Điều này có thể dẫn đến các vấn đề sử dụng bộ nhớ.

Nếu bạn cần một cá thể có cùng thời gian như một yêu cầu, tôi sẽ đề nghị sử dụng HttpContext.Current.Itemsbộ sưu tập. Đây là do thiết kế có nghĩa là một nơi để lưu trữ những thứ mà bạn cần thông qua yêu cầu. Để thiết kế đẹp hơn và dễ đọc hơn, bạn có thể sử dụng mẫu Singleton để giúp bạn quản lý các mục này. Đơn giản chỉ cần tạo một lớp Singleton lưu trữ thể hiện của nó HttpContext.Current.Items. (Trong thư viện chung của tôi cho ASP.NET, tôi có một lớp SingletonRequest chung cho mục đích này).


3
Bạn có thể cung cấp một mẫu của mẫu Singleton của bạn liên quan HttpContext.Current.Items?
Airn5485

Thêm chi tiết về tình huống của tôi: trong ứng dụng web của tôi, người dùng sẽ chạy qua thư viện mã lớp sử dụng thuộc tính lớp chia sẻ. Tôi muốn thuộc tính này là dành riêng cho người dùng, nhưng tôi không muốn phải trao tài sản này cho các chức năng khác nhau. Thiết kế bạn có thể đề cập, xử lý này đúng cách?
Airn5485

Xin vui lòng, bạn có thể chia sẻ lớp SingletonRequest?
Tebo

Tôi không lưu trữ bất kỳ dữ liệu trong lớp tĩnh. Tôi chỉ sử dụng lớp tĩnh để nhận dữ liệu hoặc cài đặt dữ liệu dưới dạng lớp dữ liệu. Vậy có vấn đề gì không?
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"- Có nguồn nào cho việc này không, vì nó không có ý nghĩa và mâu thuẫn với những gì tôi đã đọc ở nơi khác. Khi AppPool được tái chế, Miền ứng dụng có liên quan bị phá hủy hoàn toàn và GC'd. Khi điều này xảy ra, bất kỳ trường hợp tĩnh liên quan nào cũng sẽ là GC'd vì gốc của chúng (AppDomain) không còn nữa. Khi một phần của nhóm tái chế, một AppDomain mới được tạo và các thể hiện tĩnh liên quan của nó được khởi tạo.
Nick

30

Các thành viên tĩnh chỉ có một phạm vi của quy trình công nhân hiện tại, do đó nó không liên quan gì đến các yêu cầu, bởi vì các yêu cầu khác nhau có thể hoặc không thể được xử lý bởi cùng một quy trình công nhân.

  • Để chia sẻ dữ liệu với một người dùng cụ thể và qua các yêu cầu, hãy sử dụng HttpContext.Cản.Session.Session.
  • Để chia sẻ dữ liệu trong một yêu cầu cụ thể, hãy sử dụng HttpContext.Cản.Items.
  • Để chia sẻ dữ liệu trên toàn bộ ứng dụng, hãy viết một cơ chế cho điều đó hoặc cấu hình IIS để hoạt động với một quy trình duy nhất và viết một ứng dụng đơn / sử dụng.

Nhân tiện, số lượng quy trình worker mặc định là 1, vì vậy đây là lý do tại sao web chứa đầy những người nghĩ rằng các thành viên tĩnh có phạm vi của toàn bộ ứng dụng.


11

Vì các loại được chứa trong một miền ứng dụng, tôi mong muốn các lớp tĩnh xuất hiện miễn là miền ứng dụng không được tái chế hoặc nếu yêu cầu được phục vụ bởi một miền ứng dụng khác.

Tôi có thể nghĩ ra một số cách để làm cho các đối tượng cụ thể theo một yêu cầu cụ thể tùy thuộc vào những gì bạn muốn làm, ví dụ: bạn có thể khởi tạo đối tượng trong Application.BeginRequest và sau đó lưu trữ nó trong đối tượng HttpRequest để có thể truy cập được tất cả các đối tượng trong đường ống xử lý yêu cầu.


4

Nếu chúng không phải là duy nhất cho mỗi yêu cầu, có cách nào để cho phép chúng được không?

Không. Các thành viên tĩnh được sở hữu bởi quy trình ASP.NET và được chia sẻ bởi tất cả người dùng ứng dụng Web. Bạn sẽ cần phải chuyển sang các kỹ thuật quản lý phiên khác như biến phiên.


1

Thông thường các phương thức tĩnh, thuộc tính và các lớp là phổ biến ở Applicationcấp độ. Miễn là ứng dụng này tồn tại, chúng được chia sẻ.

Bạn có thể chỉ định một hành vi khác bằng cách sử dụng ThreadStaticthuộc tính. Trong trường hợp đó, chúng sẽ dành riêng cho luồng hiện tại, theo tôi, là cụ thể cho từng yêu cầu.
Tôi sẽ không khuyên điều này mặc dù nó có vẻ quá phức tạp.

Bạn có thể sử dụng HttpContext.Current.Itemsđể thiết lập nội dung cho một yêu cầu hoặc HttpContext.Current.Sessionđể thiết lập nội dung cho một người dùng (qua các yêu cầu).

Tuy nhiên, nói chung, trừ khi bạn phải sử dụng những thứ như thế Server.Transfer, cách tốt nhất là về cơ bản tạo ra mọi thứ một lần và sau đó chuyển chúng một cách rõ ràng thông qua việc gọi phương thức.


3
Jon Skeet cho chúng ta thấy rằng ThreadStatic không bao giờ an toàn trong stackoverflow
ASP.Net.com/questions/4791208/ Mark

Bỏ phiếu vì một chủ đề không phải là duy nhất cho một yêu cầu. Xin vui lòng, loại bỏ trả lời này
seebiscuit
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.