简体   繁体   中英

ASP.NET maintaining static variables

Recently we learned about AppDomain Recycling of IIS and how it affects static variables setting them to their primary values (nulls, 0s, etc).

We use some static variables that are initialized in a static constructor (for first time initialization, configuration values like "number of decimal places", "administrator email", etc... that are retrieved from DB) and then only read their value along the website execution.

Whats the best way of solving this problem? Some possible ideas:

  • Checking if variable is null/0 at each retrieval (don't like it because of a possible performance impact + time spent to add this check to each variable + code overload added to the project)

  • Somehow preventing AppDomain Recycling (this reset logic doesn't happen in Windows forms with static variables, shouldn't it work similarly as being the same language in both environments? At least in terms of standards as static variables management)

  • Using some other way of holding these variables (but we think that for being some values used for info as global reference for all users, static variables were the best option performance/coding wise)

  • Subscribing to an event that is triggered in those AppDomain Recycling so we can reinitialize all those variables (maybe best option if recycling can't be prevented...)

Ideas?

I would go with the approach that you don't like.

Checking if variable is null/0 at each retrieval (don't like it because of a possible performance impact + time spent to add this check to each variable + code overload added to the project)

  • I think it's faster than retireving from web.config.
  • You get a typed object

Its not a performance impact as you are not going to database on every retrieval request. You'll go to database (or any source) only when you find that current value set to its default value.

Checking the null wrapped into code:

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

Is there any reason why you can't store these values in your web.config file and use ConfiguationManager.AppSettings to retrieve them?

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

In view of your edit, why not cache the required values when they're first retrieved?

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

It sounds like you need a write-through (or write-behind) cache, which can be done with static variables.

Whenever a user changes the value, write it back to the database. Then, whenever the AppPool is recycled (which is a normal occurrence and shouldn't be avoided), the static constructors can read the current values from the database.

One thing you'll have to consider: If you ever scale out to a web farm, you'll need to have some sort of "trigger" when a shared variable changes so the other servers on the farm can know to retrieve the new values from the server.

Comments on other parts of your question:

(don't like [Checking if variable is null/0 at each retrieval] because of a possible performance impact + time spent to add this check to each variable + code overload added to the project

If you use a write-through cache you won't need this, but in either case The time spent to check a static variable for 0 or null should be negligible.

[AppDomain recycling] doesn't happen in Windows forms with static variables, shouldn't it work similarly as being the same language in both environments?

No, WebForms and WinForms are completely different platforms with different operating models. Web sites should be able to respond to many (up to millions) of concurrent users. WinForms are built for single-user access.

've resolved this kind of issue, following a pattern similar to this. This enabled me to cater for handling circumstances where the data could change. I set up my ISiteSettingRepository in the bootstrapper. In 1 application I get the configuration from an XML file but in others I get it from the database, as and when I need it.

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

}

Then a "Service" Interface to

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

I would take a totally different approach, one that doesn't involve anything static .

First create a class to strongly-type the configuration settings you're after:

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

Then abstract away the persistence layer by creating some repository:

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

The classes that can read and write these settings can then statically declare that they depend on an implementation of this repository:

public class SomeClass
{
    private readonly IMyConfigRepository _repo;

    public MyClass(IMyConfigRepository repo)
    {
        _repo = repo;
    }

    public void DoSomethingThatNeedsTheConfigSettings()
    {
        var settings = _repo.Load();
        //...
    }
}

Now implement the repository interface the way you want (today you want the settings in a database, tomorrow might be serializing to a .xml file, and next year using a cloud service) and the config interface as you need it.

And you're set: all you need now is a way to bind the interface to its implementation. Here's a Ninject example (written in a NinjectModule -derived class' Load method override):

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

Then, you can just swap the implementation for a MyConfigCloudRepository or a MyConfigXmlRepository implementation when/if you ever need one.

Being an asp.net application, just make sure you wire up those dependencies in your Global.asax file (at app start-up), and then any class that has a IMyConfigRepository constructor parameter will be injected with a MyConfigSqlRepository which will give you MyConfigImplementation objects that you can load and save as you please.

If you're not using an IoC container, then you would just new up the MyConfigSqlRepository at app start-up, and manually inject the instance into the constructors of the types that need it.

The only thing with this approach, is that if you don't already have a DependencyInjection-friendly app structure, it might mean extensive refactoring - to decouple objects and eliminate the new ing up of dependencies, making unit tests much easier to get focused on a single aspect, and much easier to mock-up the dependencies... among other advantages.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM