【问题标题】:ASP.NET maintaining static variablesASP.NET 维护静态变量
【发布时间】:2013-06-21 12:25:41
【问题描述】:

最近我们了解了 IIS 的 AppDomain Recycling 以及它如何影响将静态变量设置为其主要值(null、0 等)。

我们使用一些在静态构造函数中初始化的静态变量(对于第一次初始化,配置值,如“小数位数”、“管理员电子邮件”等......从数据库中检索)然后只读取他们在网站执行过程中的价值。

解决这个问题的最佳方法是什么?一些可能的想法:

  • 在每次检索时检查变量是否为 null/0(不喜欢它,因为可能会影响性能 + 将此检查添加到每个变量所花费的时间 + 添加到项目中的代码重载)

  • 1234563 )
  • 使用其他方式来保存这些变量(但我们认为,对于作为所有用户全局参考的信息的某些值,静态变量是性能/编码方面的最佳选择)

    李>
  • 订阅在这些 AppDomain Recycling 中触发的事件,以便我们可以重新初始化所有这些变量(如果无法阻止回收,这可能是最佳选择...)

想法?

【问题讨论】:

  • 将它们封装为非静态类的公共 get-only 属性,作为依赖项传递到任何需要的地方?为什么它必须是静态的?
  • “在两种环境中都一样” - 不是真的,它们非常不同!在 winforms 中,每个用户都有他/她自己的应用程序实例和它自己的一组静态变量。这经常重新启动(每次用户结束应用程序时)。在网络表单中,有一个单独的应用程序用于所有请求,因此静态变量在所有用户之间共享。除了回收之外,该 web 应用程序将永远存在。

标签: c# asp.net variables iis static


【解决方案1】:

我会采用你不喜欢的方法。

在每次检索时检查变量是否为 null/0(不喜欢它,因为可能会影响性能 + 将此检查添加到每个变量所花费的时间 + 添加到项目中的代码重载)

  • 我认为这比从 web.config 中退出要快。
  • 你得到一个类型化的对象

它不会对性能产生影响,因为您不会在每个检索请求上都访问数据库。只有当您发现当前值设置为其默认值时,您才会转到数据库(或任何来源)。

【讨论】:

  • 是的,您可以将所有配置值包装到一个对象中,您将作为单例检索该对象。每个应用程序池回收很少的查询并不是什么大问题。
【解决方案2】:

检查包装到代码中的空值:

public interface IMyConfig {
  string Var1 { get; }
  string Var2 { get; }
}

public class MyConfig : IMyConfig {
  private string _Var1;
  private string _Var2;

  public string Var1 { get { return _Var1; } }
  public string Var2 { get { return _Var2; } }

  private static object s_SyncRoot = new object();
  private static IMyConfig s_Instance;

  private MyConfig() {
    // load _Var1, _Var2 variables from db here
  }

  public static IMyConfig Instance {
    get {
      if (s_Instance != null) {
        return s_Instance;
      }
      lock (s_SyncRoot) {
        s_Instance = new MyConfig();
      }
      return s_Instance;
    }
  }
}

【讨论】:

  • 最后我会做这样的事情。检查 getter 并在 null 调用初始化函数的情况下再次为静态变量设置正确的值......但我一直认为这是一个设计缺陷:/
【解决方案3】:

您是否有任何理由不能将这些值存储在您的web.config 文件中并使用ConfiguationManager.AppSettings 检索它们?

ConfigurationManager.AppSettings["MySetting"] ?? "defaultvalue";

鉴于您的编辑,为什么不在首次检索所需的值时缓存它们?

var val = HttpContext.Cache["MySetting"];
if (val == null)
{
    val = // Database retrieval logic
    HttpContext.Cache["MySetting"] = val;
}

【讨论】:

  • 是的,这些变量由我们的 CRM 应用程序中的用户更改,因此我们必须从其数据库中检索它们,它们很少更改,但您永远不知道他们何时会这样做...
  • @AntP 缓存不会像其他静态属性一样在 AppPool 回收后继续存在。
  • @DStanley 我不确定我在哪里暗示它会?事实上,我发布的代码是考虑到这一事实而明确编写的。
【解决方案4】:

听起来您需要一个直写(或后写)缓存,这可以通过静态变量来完成。

每当用户更改值时,将其写回数据库。然后,只要 AppPool 被回收(这是正常现象,不应避免),静态构造函数就可以从数据库中读取当前值。

您必须考虑的一件事:如果您曾经扩展到网络场,当共享变量发生更改时,您需要有某种“触发器”,以便场中的其他服务器知道要检索来自服务器的新值。

对您问题的其他部分的评论:

(不喜欢 [在每次检索时检查变量是否为 null/0],因为可能会影响性能 + 为每个变量添加此检查所花费的时间 + 添加到项目中的代码重载

如果您使用直写缓存,则不需要此缓存,但在任何一种情况下,检查静态变量是否为 0 或 null 所花费的时间应该可以忽略不计。

[AppDomain 回收] 不会发生在具有静态变量的 Windows 窗体中,它不应该像在两种环境中使用相同的语言一样工作吗?

不,WebForms 和 WinForms 是完全不同的平台,具有不同的操作模型。网站应该能够响应许多(多达数百万)并发用户。 WinForms 是为单用户访问而构建的。

【讨论】:

  • 但是对于开发人员来说,在没有某种事件依赖于开发人员的检查逻辑的情况下重置变量似乎是不可靠的。甚至检查变量,如果您只是为变量设置了一些值并且 AppDomain 在 1 秒后重新调用会怎样?你失去了没有选择的价值。请记住,在这种情况下,值是从另一个来源而不是我们的网站更改的,因此我们只能从数据库中获取值(只读)
【解决方案5】:

已经解决了此类问题,遵循与此类似的模式。这使我能够处理数据可能发生变化的情况。我在引导程序中设置了我的 ISiteSettingRepository。在 1 个应用程序中,我从 XML 文件中获取配置,但在其他应用程序中,我在需要时从数据库中获取配置。

public class ApplicationSettings      
{
   public ApplicationSettings()
   {

   }
   public ApplicationSettings(ApplicationSettings settings)
   {
       ApplicationName = settings.ApplicationName;
       EncryptionAlgorithm = settings.EncryptionAlgorithm;
       EncryptionKey = settings.EncryptionKey;
       HashAlgorithm = settings.HashAlgorithm;
       HashKey = settings.HashKey;
       Duration = settings.Duration;
       BaseUrl = settings.BaseUrl;
       Id = settings.Id;

   }

 public string ApplicationName { get; set; }
 public string EncryptionAlgorithm { get; set; }
 public string EncryptionKey { get; set; }
 public string HashAlgorithm { get; set; }
 public string HashKey { get; set; }
 public int Duration { get; set; }
 public string BaseUrl { get; set; }
 public Guid Id { get; set; }

}

然后是一个“服务”接口

 public interface IApplicaitonSettingsService
{
   ApplicationSettings Get();
}

 public class ApplicationSettingsService : IApplicaitonSettingsService 
{
    private readonly ISiteSettingRepository _repository;

    public ApplicationSettingsService(ISiteSettingRepository repository)
    {
        _repository = repository;
    }

    public ApplicationSettings Get()
    {
        SiteSetting setting = _repository.GetAll();
        return setting;
    }
}

【讨论】:

    【解决方案6】:

    我会采取完全不同的方法,不涉及任何东西static

    首先创建一个类来强类型化你需要的配置设置:

    public class MyConfig
    {
        int DecimalPlaces { get; set; }
        string AdministratorEmail { get; set; }
        //...
    }
    

    然后通过创建一些存储库来抽象出持久层:

    public interface IMyConfigRepository
    {
        MyConfig Load();
        void Save(MyConfig settings);
    }
    

    可以读取和写入这些设置的类可以静态声明它们依赖于此存储库的实现:

    public class SomeClass
    {
        private readonly IMyConfigRepository _repo;
    
        public MyClass(IMyConfigRepository repo)
        {
            _repo = repo;
        }
    
        public void DoSomethingThatNeedsTheConfigSettings()
        {
            var settings = _repo.Load();
            //...
        }
    }
    

    现在以您想要的方式实现存储库接口(今天您希望将设置保存在数据库中,明天可能会序列化为 .xml 文件,明年使用云服务)和您需要的配置接口。

    你已经准备好了:你现在需要的只是一种将接口绑定到它的实现的方法。这是一个 Ninject 示例(写在一个NinjectModule-派生类'Load 方法覆盖):

    Bind<IMyConfigRepository>().To<MyConfigSqlRepository>();
    

    然后,您可以在/如果需要时将实现换成MyConfigCloudRepositoryMyConfigXmlRepository 实现。

    作为一个 asp.net 应用程序,只需确保将这些依赖项连接到 Global.asax 文件中(在应用程序启动时),然后任何具有 IMyConfigRepository 构造函数参数的类都将被注入 @ 987654332@ 将为您提供 MyConfigImplementation 对象,您可以随意加载和保存。

    如果您不使用 IoC 容器,那么您只需在应用启动时将 new 升级到 MyConfigSqlRepository,然后手动将实例注入到需要它的类型的构造函数中。

    这种方法唯一的问题是,如果您还没有一个对 DependencyInjection 友好的应用程序结构,这可能意味着需要进行大量重构 - 解耦对象并消除 newing 依赖关系,从而使单元测试更多更容易专注于单个方面,更容易模拟依赖关系......以及其他优势。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-10
      • 1970-01-01
      • 2010-10-25
      • 2012-01-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多