【问题标题】:C# Class Library - Singleton Design PatternC# 类库 - 单例设计模式
【发布时间】:2011-12-18 10:16:02
【问题描述】:

背景/问题:

我对单例设计模式还很陌生。我曾在 Web 应用程序中使用过它(在 SO 社区的帮助下):

public static AppGlobal Instance
{
    get
    {
        if (HttpContext.Current.Session != null)
        {
            HttpSessionState session = HttpContext.Current.Session;

            if (session["AppGlobalInstance"] == null)
            {
                session["AppGlobalInstance"] = new AppGlobal();
            }

            return (AppGlobal)session["AppGlobalInstance"];
        }
        else
        {
            return null;
        }
    }
}

上面的实现对我来说很有意义,因为AppGlobal 的实例存储在会话中。当会话终止时,AppGlobal 终止。如果我在 Web 应用程序调用的类库 中使用相同的设计模式会发生什么?例如,用户请求一个页面,该页面调用不知道会话的 DLL 中的方法。存储在单例实例中的数据是否会通过多次调用进行持久化?

private static readonly Singleton instance = new Singleton();
private Singleton() { }

public static Singleton Instance
{
    get
    {
        return instance;
    }
}

其他信息:

这就是我想要完成的事情:我有一个 Web 应用程序,它将接收来自第三方应用程序的 XML 请求。此 XML 将告诉我的 Web 应用程序执行三件事之一(或全部三件事)。我想要一个类的单例实例,它存储可以被多个类访问的数据。我希望单例实例在每次请求后 DIE 。如果上述方法不能实现这一点,那么实现它的最佳方法是什么?

注意:此 Web 应用程序在单个服务器上运行,并且永远不会在场中运行。

编辑 1:

根据下面的建议,我使用System.Web.HttpContext.Current.Session 来存储我的类实例。对于每个会话都是唯一的单例,这看起来是正确的方法吗(记住我在类库中)?

    public static Ariba Instance
    {
        get
        {
            if (HttpContext.Current.Session != null)
            {
                HttpSessionState session = HttpContext.Current.Session;

                if (session["AribaInstance"] == null)
                {
                    session["AribaInstance"] = new Ariba();
                }

                return (Ariba)session["AribaInstance"];
            }
            else
            {
                return null;
            }
        }
    }

【问题讨论】:

  • 您提到您希望单身人士在每个请求之后死亡。如果是这种情况,您不需要在会话中存储任何内容。相反,您可以将对象存储在 HttpContext.Current.Items["AribaInstance"] 中。我要补充一点,如果您确实使用 HttpContext.Current,那么您将在对类库进行单元测试时遇到一些困难,因为 HttpContext.Current 不会在 ASP.NET 之外填充(Session 也是如此。)如果单元测试对您很重要,您需要包装上下文和会话对象。
  • @AndyWilson,感谢您的提醒和回答!

标签: c# design-patterns singleton


【解决方案1】:

它将通过多次调用保持不变,但有一个警告。静态变量的作用域是 AppDomain,因此任何时候回收 IIS 工作进程,存储在静态变量中的所有数据都将丢失。会话数据也是如此,如果您将其存储在“进程中”。

如果您想要一个仅在 HTTP 请求期间存在的对象,您可以使用 HttpContext.Items 属性。

【讨论】:

    【解决方案2】:

    因为单例是静态的,所以您的数据将可用于 Web 应用程序中的所有请求,因此它不会仅可用于会话。

    但在 ASP.NET 应用程序中,您应该避免使用单例。相反,您应该使用 Application 对象。这样做的主要原因是,如果您将使用网络农场,那么您的单例就不再是应用范围的单例,而只是在机器上。

    【讨论】:

    • 你在农场中使用应用程序状态的想法是错误的,你是说会话状态吗?
    • 可扩展性 - 应用程序状态不会在为同一个应用程序服务的多个服务器之间共享,如在 Web 场中,或者在为同一应用程序服务的多个工作进程之间共享相同的服务器,就像在网络花园中一样。因此,您的应用程序不能依赖于包含相同数据的应用程序状态来获取跨不同服务器或进程的应用程序状态。如果您的应用程序将在多处理器或多服务器环境中运行,请考虑对必须在整个应用程序中保持保真度的数据使用更具可扩展性的选项,例如数据库。
    【解决方案3】:

    哦! 如果您想根据请求使用实例,为什么不将它作为参数传递给您正在调用的方法或作为需要 xml.xml 的类的构造函数参数。我认为这将是最好的设计方法。

    【讨论】:

    • 我想过这个,但我不想在我创建的每个方法的签名中添加 8 个参数。
    • 在这种情况下,是时候开始考虑使用依赖注入了。大多数 DI 框架允许您配置具有“每个 Web 请求”生活方式的对象,它们会将其注入到依赖于它的类型中。这使您不必将其传递给调用堆栈中的所有方法(称为方法注入)。
    • 好的。假设您不想将它们添加到构造函数中。您可以拥有一个具有这 8 个依赖项的类,并且仅在签名中传递该类。使用这种方法很容易对其进行单元测试。如果您想继续坚持一种“静态”方式来获取它们,则需要将对象放入 HttpContext.Current.Items 集合中: public static MyObject { get { return (MyObject)HttpContext.Current.Items[ "MyObject"];} 设置 { HttpContext.Current.Items.Add("MyObject",value); } } HttpContext.Current.Items 是只存在于请求中的字典。
    • 您可能需要在应用程序的 Request_end 事件(即在 global.asax 文件中)清除(也许处置其中一些对象?)
    猜你喜欢
    • 2010-11-03
    • 2017-10-12
    • 1970-01-01
    • 2011-06-25
    • 1970-01-01
    相关资源
    最近更新 更多