最近我们了解了IIS的AppDomain回收以及它如何影响静态变量,将它们设置为主要值(nulls,0s等)。
我们使用一些在静态构造函数中初始化的静态变量(用于首次初始化,配置值,如“小数位数”,“管理员电子邮件”等等...从DB中检索)然后只读取他们在网站执行中的价值。
解决这个问题的最佳方法是什么?一些可能的想法:
在每次检索时检查变量是否为null / 0(由于可能的性能影响+将此检查添加到每个变量所花费的时间+添加到项目中的代码重载,所以不喜欢它)
以某种方式阻止AppDomain回收(这种重置逻辑不会发生在带有静态变量的Windows表单中,它不应该像两种环境中的相同语言一样工作吗?至少在标准方面如静态变量管理)
使用其他一些方法来保存这些变量(但我们认为,对于所有用户使用的信息作为全局参考的一些值,静态变量是最佳选择性能/编码方式)
订阅在这些AppDomain回收中触发的事件,以便我们可以重新初始化所有这些变量(如果无法阻止回收,可能是最佳选择......)
想法?
答案 0 :(得分:3)
我会选择你不喜欢的方法。
在每次检索时检查变量是否为null / 0(由于可能的性能影响+将此检查添加到每个变量所花费的时间+添加到项目中的代码重载,所以不喜欢它)
它不会影响性能,因为您不会在每个检索请求上使用数据库。只有当您发现当前值设置为其默认值时,才会转到数据库(或任何源)。
答案 1 :(得分:1)
检查包装成代码的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;
}
}
}
答案 2 :(得分:0)
是否有任何理由无法将这些值存储在web.config
文件中,并使用ConfiguationManager.AppSettings
来检索这些值?
ConfigurationManager.AppSettings["MySetting"] ?? "defaultvalue";
鉴于您的编辑,为什么不在首次检索时缓存所需的值?
var val = HttpContext.Cache["MySetting"];
if (val == null)
{
val = // Database retrieval logic
HttpContext.Cache["MySetting"] = val;
}
答案 3 :(得分:0)
听起来你需要一个直写(或后写)缓存,这可以用静态变量来完成。
每当用户更改该值时,请将其写回数据库。然后,每当AppPool被回收时(这是正常现象并且不应该避免),静态构造函数可以从数据库中读取当前值。
您必须考虑的一件事:如果您扩展到Web场,当共享变量发生更改时,您需要具有某种“触发器”,以便场上的其他服务器可以知道检索来自服务器的新值。
对您问题其他部分的评论:
(不喜欢[在每次检索时检查变量是否为空/ 0],因为可能的性能影响+将此检查添加到每个变量所花费的时间+添加到项目中的代码重载
如果使用直写式缓存,则不需要这样,但在任何一种情况下,检查静态变量0或null所花费的时间应该可以忽略不计。
[AppDomain recycling]在带有静态变量的Windows窗体中不会发生,它是否应该在两种环境中的工作方式相同?
不,WebForms和WinForms是完全不同的平台,具有不同的操作模型。网站应该能够响应许多(多达数百万)并发用户。 WinForms是为单用户访问而构建的。
答案 4 :(得分:0)
已经按照与此类似的模式解决了这类问题。这使我能够满足数据可能发生变化的处理环境。我在引导程序中设置了我的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;
}
}
答案 5 :(得分:0)
我会采取一种完全不同的方法,一种不涉及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文件,明年可能会使用云服务),并根据需要实现配置界面。 / p>
你已经设置好了:你现在需要的只是一种将接口绑定到它的实现的方法。这是一个Ninject示例(用NinjectModule
- 派生类'Load
方法覆盖编写):
Bind<IMyConfigRepository>().To<MyConfigSqlRepository>();
然后,当您需要时,可以交换MyConfigCloudRepository
或MyConfigXmlRepository
实现的实现。
作为一个asp.net应用程序,只需确保在Global.asax
文件中(在应用启动时)连接这些依赖项,然后任何具有IMyConfigRepository
构造函数参数的类将是注入一个MyConfigSqlRepository
,它会为您提供MyConfigImplementation
个对象,您可以根据需要加载和保存。
如果您没有使用IoC容器,那么您只需在应用启动时new
向上MyConfigSqlRepository
,然后手动将实例注入需要它的类型的构造函数中。
这种方法的唯一之处在于,如果你还没有一个DependencyInjection友好的应用程序结构,它可能意味着广泛的重构 - 去除对象并消除new
依赖关系,使单位测试更容易集中在一个方面,并且更容易模拟依赖项......以及其他优点。