CustomApplicationSettingsBase

CustomApplicationSettingsBase

更新了问题(以正确指出问题)

我正在使用一个lib,它实现了一个从ApplicationSettingsBase派生的类。

namespace MyLib {
    public sealed class GlobalLibSettings : ApplicationSettingsBase
    {
        [UserScopedSetting, DefaultSettingValue("true")]
        public bool SimpleProperty{
            get { return (bool) this["SimpleProperty"]; }
            set {
                this["SimpleProperty"] = value;
                Save();
            }
        }
    }
}


现在,我在另一个项目中使用此库。这些项目还包含至少一个从ApplicationSettingsBase派生的类。

namespace MyProject {
    public sealed class ProjectSettings : ApplicationSettingsBase
    {
        [UserScopedSetting, DefaultSettingValue("true")]
        public bool AnotherProperty{
            get { return (bool) this["AnotherProperty"]; }
            set {
                this["AnotherProperty"] = value;
                Save();
            }
        }
    }
}


现在,两个从ApplicationSettingsBase派生的类都将其属性存储到相同的user.config文件中。该应用程序和lib使用多个任务,并且如果两个任务同时执行(例如)属性设置器,则会出现以下异常。这两个任务都尝试同时执行写操作...

System.Configuration.ConfigurationErrorsException occurred
  BareMessage=Beim Laden einer Konfigurationsdatei ist ein Fehler aufgetreten.: Der Prozess kann nicht auf die Datei "... _xneb3g43uoxqiagk4ge5e4hea1vxlina\1.0.4.862\user.config" zugreifen, da sie von einem anderen Prozess verwendet wird.
  Filename=..._xneb3g43uoxqiagk4ge5e4hea1vxlina\1.0.4.862\user.config
  HResult=-2146232062
  Line=0
  Message=Beim Laden einer Konfigurationsdatei ist ein Fehler aufgetreten.: Der Prozess kann nicht auf die Datei "..._xneb3g43uoxqiagk4ge5e4hea1vxlina\1.0.4.862\user.config" zugreifen, da sie von einem anderen Prozess verwendet wird. (..._xneb3g43uoxqiagk4ge5e4hea1vxlina\1.0.4.862\user.config)
  Source=System.Configuration
  StackTrace:
       bei System.Configuration.ConfigurationSchemaErrors.ThrowIfErrors(Boolean ignoreLocal)
  InnerException:
       HResult=-2147024864
       Message=Der Prozess kann nicht auf die Datei "_xneb3g43uoxqiagk4ge5e4hea1vxlina\1.0.4.862\user.config" zugreifen, da sie von einem anderen Prozess verwendet wird.
       Source=mscorlib
       StackTrace:
            bei System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
            bei System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
            bei System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)
            bei System.Configuration.Internal.InternalConfigHost.StaticOpenStreamForRead(String streamName)
            bei System.Configuration.Internal.InternalConfigHost.System.Configuration.Internal.IInternalConfigHost.OpenStreamForRead(String streamName, Boolean assertPermissions)
            bei System.Configuration.Internal.InternalConfigHost.System.Configuration.Internal.IInternalConfigHost.OpenStreamForRead(String streamName)
            bei System.Configuration.ClientConfigurationHost.OpenStreamForRead(String streamName)
            bei System.Configuration.BaseConfigurationRecord.RefreshFactoryRecord(String configKey)


我可以在以下情况下重现它:

var settings1 = new GlobalLibSettings ();
var settings2 = new ProjectSettings ();
Task.Factory.StartNew(()=>{
    while(true) settings1.SimpleProperty = !settings1.SimpleProperty;
});
Task.Factory.StartNew(()=>{
    while(true) settings2.AnotherProperty = !settings2.AnotherProperty;
});


现在,我正在寻找一种实现来保护对user.config文件的访问的实现。

解:

我找到了可行的解决方案。 CustomApplicationSettingsBase锁定并发任务。

public sealed class GlobalLibSettings : CustomApplicationSettingsBase

public sealed class ProjectSettings : CustomApplicationSettingsBase具有:

namespace MyLib {
    public static class LockProvider
    {
        public static object AppSettingsLock { get; } = new object();
    }

    public class CustomApplicationSettingsBase : ApplicationSettingsBase
    {
        public override object this[string propertyName] {
            get {
                lock (LockProvider.AppSettingsLock) {
                    return base[propertyName];
                }
            }
            set {
                lock (LockProvider.AppSettingsLock) {
                    base[propertyName] = value;
                }
            }
        }
        public override void Save() {
            lock (LockProvider.AppSettingsLock) {
                base.Save();
            }
        }
        public override void Upgrade() {
            lock (LockProvider.AppSettingsLock) {
                base.Upgrade();
            }
        }
    }
}


谢谢你的帮助!

最佳答案

问题可能出在以下事实上:您同时具有多个GlobalAppSettings实例。这意味着可能有多个线程试图读取/写入同一文件,这导致了排他性。

带锁的解决方案不起作用,因为_locker对象不在GlobalAppSettings的不同实例之间共享。

我看到以下解决方案。首先,您可以在第二次编辑中执行类似的操作,即使用静态对象同步对设置的访问。但是,我更喜欢第二种解决方案。尝试将CustomApplicationSettingsBase实现为单例。甚至更好地使用依赖项注入在需要的地方注入CustomApplicationSettingsBase实例,并告诉DI容器CustomApplicationSettingsBase应该是单例。

08-28 12:40