从大型.NET代码库中删除单例

时间:2011-11-01 00:50:31

标签: asp.net singleton

2 个答案:

答案 0 :(得分:1)

我建议首先实施“穷人的DI”模式。这是你在类中定义两个构造函数的地方,一个接受依赖项的实例(IoC),另一个是new的新构造函数(或调用单例)。

通过这种方式,您可以逐步引入IoC,并且仍然可以使用默认构造函数来完成其他所有操作。最终,当您在大多数地方使用IoC时,您可以开始删除默认构造函数(和单例)。

public class Foo {
    public Foo(ILogger log, IConfig config) {
        _logger = log;
        _config = config;
    }

    public Foo() : this(Logger.Instance(), Config.Instance()) {}
}

答案 1 :(得分:1)

通常,您应该尝试将上下文信息包装到自己的实例中,并提供静态访问器方法来引用它。例如,通过HttpContext考虑HttpContext.Current及其在Web应用程序中的每个位置都可用。

您应该尝试设计类似的东西,以便不是返回单例实例,而是从当前上下文返回实例。这样,您就不需要更改引用这些静态方法的消费者代码(例如Logger.Instance())。

我通常将记录器,当前用户,配置,安全权限等信息汇总到应用程序上下文中(如果需要,可以是多个类)。 AppContext.Current静态方法返回当前上下文。方法实现类似于

public interface IContextStorage
{
        // Gets the stored context
        AppContext Get();

        // Stores the context, context can be null
        void Set(AppContext context);
}

public class AppContext
{
    private static IContextStorage _storageProvider, _defaultStorageProvider;

    public static AppContext Current
    {
    get
    {
       var value = _storageProvider.Get();
       // If context is not available in storage then lookup
       // using default provider for worker (threadpool) therads.
       if (null == value && _storageProvider != _defaultStorageProvider
        && Thread.CurrentThread.IsThreadPoolThread)
       {
        value = _defaultStorageProvider.Get();
       }
       return value;
    }
    }

  ...
}

IContextStorage实现是特定于应用程序的。静态变量_storageProvider在应用程序启动时注入,而_defaultStorageProvider是一个查看当前调用上下文的简单实现。

应用程序上下文创建分多个阶段进行 - 例如,配置等全局信息在应用程序启动时被读取和缓存,而特定信息如用户和安全性在认证阶段形成。一旦所有信息都可用,就会创建实际实例并将其存储到特定于应用程序的存储位置。例如,桌面应用程序将使用单例实例,而Web应用程序可能会将实例存储到会话状态。对于Web应用程序,您可以在每个请求的开头都有逻辑,以确保初始化上下文。

对于可伸缩的Web应用程序,您可以拥有一个存储提供程序,它将上下文实例存储到缓存中,如果不存在于缓存中,则重新构建它。