2016-12-19 6 views
1

Обновленный Вопрос (указать проблему правильно)ApplicationSettingsBase ConfigurationErrorsException несколько сборок

Я использую 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 используют несколько задач, и если две задачи выполняют (например) свойство setter в то же время, я получаю следующее исключение. Обе задачи пытаются выполнить действие записи в то же время ...

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

Спасибо за вашу помощь!

+1

Реализация блокировки поможет только в том случае, если все потоки используют один и тот же экземпляр вашего класса. –

+1

Как создать экземпляр GlobalAppSettings? У вас есть один или несколько экземпляров GlobalAppSettings? –

+0

@Mathew Jibin и Michal Komorowski: вы оба правы, теперь я вижу свою ошибку. Мне понадобится глобальный объект блокировки для управления доступом к файлам из нескольких реализаций. Я проверю его днем. – WPFGermany

ответ

1

Проблема, вероятно, заключается в том, что у вас есть несколько экземпляров GlobalAppSettings, которые используются одновременно. Это означает, что может быть более одного потока, пытающегося прочитать/записать тот же файл, и это приводит к эксклюзии.

Ваше решение с блокировкой не работает, потому что _locker объект не используется между различными экземплярами GlobalAppSettings.

Я вижу следующие решения. Во-первых, вы можете сделать что-то вроде вашего второго редактирования, чтобы использовать статический объект для синхронизации доступа к настройкам. Однако мне больше нравится второе решение. Попробуйте реализовать CustomApplicationSettingsBase как синглтон. Или даже лучше использовать инъекцию зависимости, чтобы вставить экземпляр CustomApplicationSettingsBase, где это необходимо, и сообщить контейнеру DI, что CustomApplicationSettingsBase должен быть синглом.

+0

Я вижу проблему с реализацией Singleton. Это меньше проблем с экземплярами. Я имею несколько реализаций 'ApplicationSettingsBase' (например, в assemblyA.GlobalAppSettings, assemblyA.SpecificToolSettings, assemblyB.Settings ...). Все они указывают на тот же user.config. Многопоточный параллельный доступ к нескольким свойствам (чтение/запись) вызывает вызов 'Save()', например, путем установки свойства. Затем параллельные потоки пытаются записать в один файл и создать «Исключение». Реализация синглтона здесь не поможет. – WPFGermany

4

Существует лот, чтобы не понравиться System.Configuration, но это не делает эту деталь неправильной. То, что вы можете увидеть в Reference Source, файл открыт с:

return new FileStream(streamName, FileMode.Open, FileAccess.Read, FileShare.Read); 

Так простой доступ для чтения и использования FileShare.Read, чтобы кто-нибудь еще, чтобы читать из файла, а также. Таким образом, любой поток может инициировать этот код, вы не можете получить исключение для обмена файлами.

Таким образом, решение, которое вы пробовали, не может решить проблему. Найти другое объяснение будет сложно с предоставленной информацией. Очень сложно получить конфликт совместного доступа в файле, который так хорошо скрыт. Единственное правдоподобное объяснение состоит в том, что другой поток записывает в файл в одно и то же время. Другими словами, выполняется метод Save().

Вам должно быть необыкновенно не повезло. Но это технически возможно. Используйте окно Debug> Windows> Threads debugger, чтобы узнать, что делают другие потоки, вы должны увидеть вызов метода Save() в одной из своих трасс стека. Единственная другая возможная причуда - это экологическая угроза, которая может произвольно сделать файл недоступным при сканировании файла.

И, наконец, не совсем очевидно, что вы сделали, чтобы сделать эту работу вообще. Только проект EXE решения может использовать файл .config. Множество неочевидных hanky-panky необходимо, чтобы получить DLL для использования настроек.Мы не знаем, что вы сделали, обязательно уточните свой вопрос с этими деталями. На самом деле лучше не делать этого вообще.

+0

Спасибо за ваши предложения. Теперь я нашел проблему и решил ее с комментариями Мэтью и Михала. Правильно, что вызов 'Save()' (например) из разных 'Tasks' (протестированных с помощью циклов while) параллельно в разных реализациях' ApplicationSettingsBase' в разных сборках вызывает одновременный доступ к файлам. Я могу предотвратить это, используя 'lock', как описано в моем вопросе, если я поместил блокировку в статический класс. Я обновляю свой ответ с помощью решения, которое работает. Спасибо за ваше объяснение. – WPFGermany

+1

Исправьте это правильно, вызывая Save() в любое другое время, чем выход программы - очень плохая идея. Режим сбоя очень уродливый, вы можете сохранить настройки, которые приводят к сбою программы. он будет * всегда * сбой, когда пользователь запустит его обратно. У него нет хорошего способа решить проблему, так как он не знает файл с плохими данными, ему придется переформатировать диск. –

+0

Почему вы вызываете 'Save()' после изменения значения свойства bad? Если я использую производные классы из 'ApplicationSettingsBase' в библиотеке, которая используется другими проектами, я не буду вызывать' Save() 'классов lib из моих проектов. Поэтому я буду защищать доступ к файлу. Вызов 'Save()' из нескольких «потоков» только при выходе из программы приведет к одинаковым проблемам и компрометации файлов таким же образом, если доступ к файлу небезопасен. – WPFGermany

Смежные вопросы