现在我遇到了一个我刚刚编写的用于ASP.NET MVC的Singleton问题 - 我的Singleton看起来像这样:
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”被分配了Singleton的实例。一个新的Request实例被添加到Singleton的内部列表中。然后我们返回一个View()。下次我点击这个控制器的SomeAction()时,我期待Singleton包含我刚刚添加的SomeClass实例的List,但是List是空的。
发生了什么事?垃圾收集吞噬了我的对象吗?在ASP.NET MVC中实现Singleton模式时,我需要考虑一些特殊的东西吗?
谢谢!
编辑:啊,灯泡刚刚开始。因此,每个新的页面请求都是在一个全新的过程中进行的!得到它了。 (我的背景是桌面应用程序开发,所以这对我来说是一个不同的范例......)
EDIT2:当然,这里有一些澄清。我的应用程序需要一个请求编号系统,其中请求的内容需要一个唯一的ID,但我没有可用的数据库。但必须为每个用户提供记录每个请求的状态。我还意识到,如果用户双击请求按钮,它可以加倍作为一种规范会话的方式。单身似乎是要走的路,但是意识到每个请求都在自己的过程中基本上消除了单身。我想这也消除了静态类,对吗?
EDIT3:好的,我已经添加了我正在使用的实际代码(减去每个方法的实现,为了简单起见......)我希望这更清楚。
EDIT4:我正在向Chris发出绿色复选标记,因为我开始意识到应用程序级单例就像拥有全局(而全局是邪恶的,对吧?) - 除了开玩笑之外,最好的选择是让DB和SQLite看起来最适合现在,尽管我可以肯定看到自己将来转向Oracle实例。不幸的是,最好的选择是使用ORM,但这是另一个学习曲线。开溜。
EDIT5:上次修改,我发誓。 :-)
所以我尝试使用HttpRuntime.Cache,但很惊讶地发现我的缓存不断刷新/无效,无法弄清楚发生了什么。好吧,我正在被我正在做的其他事情的副作用绊倒:写给“Web.config”
答案 - &gt; 我不知道,当“web.config”无论如何被更改时,应用程序都是RESTARTED!是的,一切都被扔掉了。我的单身人士,我的缓存,一切。嘎。难怪没有什么工作正常。看起来回写web.config通常是不好的做法,我现在要避开。
再次感谢所有帮助我摆脱困境的人。
答案 0 :(得分:3)
单例特定于处理实例。正在为每个页面请求生成一个新实例。页面请求通常被认为是无状态的,因此来自其中的数据并不仅仅是为了另一个。
为了使它在应用程序级别工作,必须在那里声明实例变量。有关如何创建应用程序级变量的提示,请参阅this question。请注意,这将使其可用于所有请求..这并不总是您想要的。
当然,如果您尝试实现某种类型的会话状态,那么您可能只使用会话或使用某种类型的缓存过程。
<强>更新强>
根据您的编辑:静态类不应该维护数据。它的目的是简单地将一些常用方法组合在一起,但它不应该在方法调用之间存储数据。单例是一个完全不同的东西,因为它只是一个类,你只想为请求创建一个对象。
这些似乎都不是你想要的。
现在,拥有一个应用程序级单例可供整个应用程序使用,但是它会跨越请求并且必须相应地进行编码。
这几乎听起来像是在尝试构建内存数据存储。您可以沿着利用.NET Page.Cache,MemCache或Enterprise Library's Caching Application Block等各种缓存机制之一的路径前进。
然而,所有这些都有在托管应用程序的工作进程被回收的情况下被清除的问题。这可能发生在最糟糕的时候..并且将发生基于随机的事情,如内存使用,一些计时器到期,一定数量的页面重新编译等。
相反,我强烈建议使用某种类型的持久存储。是否只是您读取/写入的xml文件或将SQL Lite之类的内容嵌入到应用程序中。 SQL Lite是一个非常轻量级的数据库,不需要在服务器上安装;你只需要装配。
答案 1 :(得分:3)
您可以使用依赖注入来控制类的生命周期。如果您使用Castle Windsor,可以在web.config中添加这一行。
<component id="MySingleton" service="IMySingleton, MyInterfaceAssembly"
type="MySingleton, MyImplementationAssembly" lifestyle="Singleton" />
当然,将您的应用程序连接到使用DI的主题超出了我的答案,但要么您正在使用它,这个答案可以帮助您或者您可以在概念上达到顶峰并爱上它。 :)