ASP.NET维护静态变量

本文关键字:变量 静态 维护 NET ASP | 更新日期: 2023-09-27 18:19:27

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

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

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

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

  • 以某种方式阻止AppDomain回收(这种重置逻辑不会发生在带有静态变量的Windows窗体中,它在两种环境中的工作方式不应该类似于相同的语言吗?至少在静态变量管理的标准方面)

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

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

想法?

ASP.NET维护静态变量

我会选择你不喜欢的方法。

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

  • 我认为这比从web.config中退出要快
  • 您得到一个已键入的对象

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

检查包装到代码中的null:

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;
    }
  }
}

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

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

鉴于您的编辑,为什么不在首次检索到所需值时缓存这些值呢?

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

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

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

你必须考虑的一件事是:如果你扩展到一个web服务器场,当共享变量发生变化时,你需要有某种"触发器",这样服务器场上的其他服务器才能知道从服务器中检索新值。

对问题其他部分的评论:

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

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

[AppDomain回收]不会发生在带有静态变量的Windows窗体中,它在两种环境中的工作方式不应该类似于同一种语言吗?

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

已经按照类似的模式解决了这类问题。这使我能够处理数据可能发生变化的情况。我在引导程序中设置了ISiteSettingRepository。在一个应用程序中,我从一个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;
    }
}

我会采取一种完全不同的方法,一种不涉及任何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构造函数参数的类都将注入MyConfigSqlRepository,它将为您提供MyConfigImplementation对象,您可以随意加载和保存这些对象。

如果你不使用IoC容器,那么你只需要在应用程序启动时new向上MyConfigSqlRepository,并手动将实例注入到需要它的类型的构造函数中

这种方法唯一的好处是,如果你还没有一个DependencyInjection友好的应用程序结构,这可能意味着进行广泛的重构-解耦对象并消除依赖关系的new,使单元测试更容易集中在单个方面,也更容易模拟依赖关系。。。以及其他优点。