Updated Question (to point out the problem correctly)
I'm using a lib, which implements a class that derives from ApplicationSettingsBase
.
namespace MyLib {
public sealed class GlobalLibSettings : ApplicationSettingsBase
{
[UserScopedSetting, DefaultSettingValue("true")]
public bool SimpleProperty{
get { return (bool) this["SimpleProperty"]; }
set {
this["SimpleProperty"] = value;
Save();
}
}
}
}
Now I use this lib in another project. The projects also contains at least one class that derives from ApplicationSettingsBase
.
namespace MyProject {
public sealed class ProjectSettings : ApplicationSettingsBase
{
[UserScopedSetting, DefaultSettingValue("true")]
public bool AnotherProperty{
get { return (bool) this["AnotherProperty"]; }
set {
this["AnotherProperty"] = value;
Save();
}
}
}
}
Now both classes deriving from ApplicationSettingsBase
storing their properties to same user.config
file. The application and lib uses multiple tasks and if two tasks perform (for example) the properties setter at the same time I get the following exception. Both tasks try to perform write action at same time...
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)
I can reproduce it with the following scenario:
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;
});
Now I was looking for an implementation to protect the access to user.config
file.
Solution:
I found a working solution. The CustomApplicationSettingsBase
locks concurrent tasks.
public sealed class GlobalLibSettings : CustomApplicationSettingsBase
and
public sealed class ProjectSettings : CustomApplicationSettingsBase
with:
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();
}
}
}
}
Thanks for your help!