2

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?

2
  • 1
    Encapsulate them as public get-only properties of a non-static class that you pass around as a dependency wherever you need it? Why does it have to be static? Commented Jun 21, 2013 at 12:30
  • "same in both environments" - not really, they are very different! In winforms each user has his/her own application-instance with it's own set of static variables. This is frequently restarted (every time the user ends the app). In webforms there is a single application that is used for all requests, so static variables are shared between all users. Apart from recycling that webapp would remain alive forever. Commented Jun 21, 2013 at 12:41

6 Answers 6

3

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Yes, you can wrap all configuration values into one object, that you'll retrieve as singleton. Few queries per app pool recycle is not a big deal.
1

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

1 Comment

In the end i will do something like this. Checking at the getter and in case of null calling an Initialization function that sets the correct values to the static variables again... But I keep thinking this is a design flaw :/
0

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

3 Comments

Yes, these variables are changed by the users from our CRM Application so we have to retrieve them from its Database, they are rarely changed but you never know when they will do it...
@AntP the Cache will not survive an AppPool recycle any more than other static properties will.
@DStanley I'm not sure where I implied that it would? In fact, the code I posted is explicitly written in consideration of that fact.
0

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.

1 Comment

But it seems unreliable to the developer that the variables are reset without some kind of event relying the checking logic to the developer. And even checking the variables, what if you just set some value to a variable and the AppDomain recicled 1 second later? You lost the value without an option. And remember in this case the value gets changed from another source not our website so we only get the value from the database (read only)
0

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

Comments

0

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 newing 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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.