views:

108

answers:

2

Right now I'm having an issue with a Singleton that I just wrote for use in ASP.NET MVC -- My Singleton looks like this:

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
}

And my controller code looks like this:

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

Everything works okay the first time I hit the controller. "reqGen" is assigned the instance of the Singleton. A new instance of Request is added to the internal list of the Singleton. And then we return a View(). The next time I hit this controller's SomeAction(), I'm expecting the Singleton to contain the List with the instance of SomeClass that I had just added, but instead the List is empty.

What's happened? Has Garbage Collection gobbled up my object? Is there something special I need to consider when implementing the Singleton pattern in ASP.NET MVC?

Thanks!

EDIT: Ahh, the lightbulb just went on. So each new page request takes place in a completely new process! Got it. (my background is in desktop application development, so this is a different paradigm for me...)

EDIT2: Sure, here's some more clarification. My application needed a request number system where something being requested needed a unique ID, but I had no DB available. But it had to be available to every user to log the state of each request. I also realized that it could double as a way to regulate the session, say, if a use double-clicked the request button. A singleton seemed like the way to go, but realizing that each request is in its own process basically eliminates the singleton. And I guess that also eliminates the static class, right?

EDIT3: ok, I've added the actual code that I'm working with (minus the implementation of each Method, for simplicity sake...) I hope this is clearer.

EDIT4: I'm awarding the green check mark to Chris as I'm beginning to realize that an application-level singleton is just like having a Global (and global's are evil, right?) -- All kidding aside, the best option really is to have a DB and SQLite seems like the best fit for now, although I can definitely see myself moving to an Oracle instance in the future. Unfortunately, the best option then would be to use an ORM, but that's another learning curve to climb. bugger.

EDIT5: Last edit, I swear. :-)

So I tried using HttpRuntime.Cache, but was surprised to find that my cache was getting flushed/invalidated constantly and couldn't figure out what was going on. Well, I was getting tripped up by a side-effect of something else I was doing: Writing to "Web.config"

Unbeknownst to me, when "web.config" is altered in anyway, the application is RESTARTED! Yup, everything gets thrown away. My singleton, my cache, everything. Gah. No wonder nothing was working right. Looks like writing back to web.config is generally bad practice which I shall now eschew.

Thanks again to everyone who helped me out with this quandary.

+3  A: 

The singleton is specific to the processing instance. A new instance is being generated for each page request. Page requests are generally considered stateless so data from one doesn't just stick around for another.

In order to get this to work at the application level, the instance variable will have to be declared there. See this question for a hint on how to create an application level variable. Note that this would make it available across all requests.. which isn't always what you want.

Of course, if you are trying to implement some type of session state then you might just use session or use some type of caching procedure.

UPDATE
Based on your edits: A static class should not maintain data. It's purpose is to simply group some common methods together, but it shouldn't store data between method calls. A singleton is an altogether different thing in that it is a class that you only want one object to be created for the request.

Neither of those seem to be what you want.

Now, having an application level singleton would be available to the entire application, but that crosses requests and would have to be coded accordingly.

It almost sounds like you are trying to build an in memory data store. You could go down the path of utilizing one of the various caching mechanisms like .NET Page.Cache, MemCache, or Enterprise Library's Caching Application Block.

However, all of those have the problem of getting cleared in the event the worker process hosting the application gets recycled.. Which can happen at the worst times.. And will happen based on random things like memory usage, some timer expired, a certain number of page recompiles, etc.

Instead, I'd highly recommend using some type of persisted storage. Whether that be just xml files that you read/write from or embedding something like SQL Lite into the application. SQL Lite is a very lightweight database that doesn't require installation on the server; you just need the assemblies.

Chris Lively
So is this one of those instances in the "static class" vs. "singleton" comparison where I should implement a Static Class instead?
Pretzel
@Pretzel: without knowing what the class is or why you need exactly one copy of it it's hard to tell. What does the class do? If you really need it to be a data container then a caching mechanism or session is probably better. Although I wouldn't store classes in session. If you provide details of what you are trying to accomplish a lot of people here can give you some direction on how to get there.
Chris Lively
@Chris Lively: Thanks for the update. I've added the actual code I'm working with so you have a clearer picture of what I'm doing. Yes, I am building an in-memory data store. I wasn't planning on storing the Requests out to DB or XML (well, not yet.) The requests are being sent to users via SMTP. I'll take a look at SQL Lite. Should I look into using NHibernate or Entity Framework? Or should I just code to SQL Lite directly?
Pretzel
@Pretzel: I'm not a fan of ORM tools, but those are certainly options. Personally, I'd just go straight to SQL Lite.
Chris Lively
@Chris: Check out my latest edit above. Classic "face-palm" here. But how was I supposed to know?
Pretzel
+2  A: 

You can use Dependency Injection to control the life of the class. Here's the line you could add in your web.config if you were using Castle Windsor.

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

Of course, the topic of wiring up your application to use DI is beyond my answer, but either you're using it and this answer helps you or you can take a peak at the concept and fall in love with it. :)

Mayo
@Mayo: I started to read up on DI a few months ago and have now gotten into the habit of coding to Interfaces (rather than Concrete classes), so in certain places of my code I do "manual injection", but I still haven't managed to adopt a DI Container like Castle Windsor or Ninject. (I haven't fully groked how to use it yet...) Maybe that will be my project this weekend.
Pretzel
@Mayo: I tried doing DI with Ninject and I was able to get it to successfully inject the singleton, but as soon as the process died, the singleton died with it, so this didn't work for me.
Pretzel