Синглтоны и ASP.NET MVC - PullRequest
       25

Синглтоны и ASP.NET MVC

2 голосов
/ 16 октября 2010

Сейчас у меня проблема с синглтоном, который я только что написал для использования в ASP.NET MVC - мой синглтон выглядит так:

public sealed class RequestGenerator : IRequestGenerator
{
    // Singleton pattern
    private RequestGenerator() 
    {
        requestList = new Stack<Request>();
        appSettings = new WebAppSettings();
    }
    private static volatile RequestGenerator instance = new RequestGenerator();
    private static Stack<Request> requestList = new Stack<Request>();
    // abstraction layer for accessing web.config
    private static IAppSettings appSettings = new WebAppSettings();
    // used for "lock"-ing to prevent race conditions
    private static object syncRoot = new object();
    // public accessor for singleton        
    public static IRequestGenerator Instance
    {
        get
        {
            if (instance == null)
            {
                lock (syncRoot)
                {
                    if (instance == null)
                    {
                        instance = new RequestGenerator();
                    }
                }
            }
            return instance;
        }
    }     
    private const string REQUESTID = "RequestID";

    // Find functions
    private Request FindRequest(string component, string requestId)
    private List<Request> FindAllRequests(string component, string requestId)

    #region Public Methods required by Interface
    // Gets and increments last Request ID from Web.Config, creates new Request, and returns RequestID
    public string GetID(string component, string userId)

    // Changes state of Request to "submitted"
    public void SetID(string component, string requestId)

    // Changes state of Request to "success" or "failure" and records result for later output
    public void CloseID(string component, string requestId, bool success, string result)

    // Verifies that Component has generated a Request of this ID
    public bool VerifyID(string component, string requestId)

    // Verifies that Component has generated a Request of this ID and is owned by specified UserId
    public bool VerifyID(string component, string userId, string requestId)

    // Returns State of Request ID (Open, Submitted, etc.)
    public Status GetState(string component, string requestId)

    // Returns Result String of Success or Failure.
    public string GetResult(string component, string requestId)
    #endregion
}

И мой код контроллера выглядит так:

public ViewResult SomeAction()
{
    private IRequestGenerator reqGen = RequestGenerator.Instance;
    string requestId = reqGen.GetID(someComponentName, someUserId);
    return View(requestId);
}

Все работает хорошо, когда я впервые нажимаю на контроллер. «reqGen» назначен экземпляр синглтона. Новый экземпляр Request добавляется во внутренний список Singleton. И тогда мы возвращаем View (). В следующий раз, когда я нажму SomeAction () этого контроллера, я ожидаю, что Singleton будет содержать список с экземпляром SomeClass, который я только что добавил, но вместо этого список пуст.

Что случилось? Сбор мусора поглотил мой объект? Есть ли что-то особенное, что мне нужно учитывать при реализации шаблона Singleton в ASP.NET MVC?

Спасибо!

РЕДАКТИРОВАТЬ: Ах, лампочка только что зажглась. Таким образом, каждый новый запрос страницы происходит в совершенно новом процессе! Понял. (мой опыт в разработке настольных приложений, поэтому для меня это другая парадигма ...)

РЕДАКТИРОВАТЬ 2: Конечно, вот еще несколько разъяснений. Моему приложению требовалась система нумерации запросов, в которой запрашиваемому требовался уникальный идентификатор, но у меня не было доступной БД. Но он должен был быть доступен каждому пользователю для регистрации состояния каждого запроса. Я также понял, что это может стать способом регулирования сессии, скажем, если пользователь дважды щелкнет кнопку запроса. Синглтон показался мне подходящим, но понимание того, что каждый запрос выполняется в своем собственном процессе, в основном устраняет синглтон. И я думаю, что это также устраняет статический класс, верно?

EDIT3: хорошо, я добавил реальный код, с которым я работаю (за исключением реализации каждого метода, для простоты ...) Надеюсь, это будет понятнее.

EDIT4: Я назначаю зеленую галочку Крису, когда начинаю понимать, что синглтон уровня приложения - это все равно, что иметь глобальный (а глобальные - это зло, верно?) - - Шутка в сторону, лучший вариант на самом деле - это иметь БД, и SQLite покажется вам наиболее подходящим, хотя я определенно могу видеть, что в будущем перейду на экземпляр Oracle. К сожалению, тогда лучшим вариантом будет использование ORM, но это еще одна кривая обучения, которую нужно преодолеть. мудак.

EDIT5: Последнее редактирование, клянусь. :-)

Поэтому я попытался использовать HttpRuntime.Cache, но был удивлен, обнаружив, что мой кэш постоянно очищался / становился недействительным и не мог понять, что происходит. Что ж, меня сбило с толку побочным эффектом чего-то другого, что я делал: запись в "Web.config"

Ответ -> Без ведома меня, когда в любом случае изменяется «web.config», приложение перезапускается! Да, все выбрасывается. Мой синглтон, мой кеш, все. Гах. Не удивительно, что ничего не получалось. Похоже, что писать обратно в web.config - это вообще плохая практика, которую я сейчас буду избегать.

Еще раз спасибо всем, кто помог мне с этим затруднением.

Ответы [ 2 ]

3 голосов
/ 16 октября 2010

Вы можете использовать Dependency Injection, чтобы контролировать жизнь класса.Вот строка, которую вы могли бы добавить в свой web.config, если бы вы использовали Castle Windsor.

<component id="MySingleton" service="IMySingleton, MyInterfaceAssembly" 
type="MySingleton, MyImplementationAssembly" lifestyle="Singleton" />

Конечно, тема подключения вашего приложения для использования DI выходит за рамки моего ответа, но выиспользование этого, и этот ответ поможет вам, или вы можете получить представление о концепции и влюбиться в нее.:)

3 голосов
/ 16 октября 2010

Синглтон относится к экземпляру обработки. Новый экземпляр создается для каждого запроса страницы. Запросы на страницы, как правило, считаются без сохранения состояния, поэтому данные от одного не просто сохраняются для другого.

Чтобы заставить это работать на уровне приложения, переменная экземпляра должна быть объявлена ​​там. См. этот вопрос , чтобы узнать, как создать переменную уровня приложения. Обратите внимание, что это сделает его доступным для всех запросов ... что не всегда то, что вы хотите.

Конечно, если вы пытаетесь реализовать какой-либо тип состояния сеанса, вы можете просто использовать сеанс или использовать какой-либо тип процедуры кэширования.

UPDATE
Исходя из ваших правок: статический класс не должен поддерживать данные. Его цель - просто сгруппировать некоторые общие методы, но они не должны хранить данные между вызовами методов. Синглтон - это совсем другое, потому что это класс, в котором вы хотите, чтобы для запроса был создан только один объект.

Ни один из них, кажется, не то, что вы хотите.

Теперь наличие синглтона уровня приложения будет доступно для всего приложения, но оно пересекает запросы и должно быть соответствующим образом закодировано.

Похоже, вы пытаетесь создать хранилище данных в памяти. Вы можете пойти по пути использования одного из различных механизмов кэширования, таких как .NET Page.Cache , MemCache или Блок приложения кэширования Enterprise Library .

Однако все они имеют проблему очистки в случае повторного использования рабочего процесса, на котором размещено приложение. Это может произойти в худшие времена. И это произойдет из-за случайных вещей, таких как использование памяти, истечение некоторого таймера , определенное количество страниц перекомпилируется и т. д.

Вместо этого я настоятельно рекомендую использовать какой-либо тип постоянного хранилища. Будь то просто XML-файлы, которые вы читаете / пишете, или встраиваете что-то вроде SQL Lite в приложение. SQL Lite - очень легкая база данных, которая не требует установки на сервере; вам просто нужны сборки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...