Сообщений 1    Оценка 585 [+0/-1]         Оценить  
Система Orphus

Client Settings FAQ

Автор: Raghavendra Prabhu
Microsoft Corporation

Перевод: Алексей Кирюшкин
The RSDN Group

Источник: Cool Client Stuff
Опубликовано: 30.07.2006
Исправлено: 08.08.2006
Версия текста: 1.0

Я заметил, что есть два вида параметров - настройки приложения (Application scoped) и настройки пользователя (User scoped). Причем настройки приложения, кажется только для чтения, и я не могу изменить их во время работы приложения. Почему так?
Вы сказали, что файлы user.config находятся в папке с данными пользователя. Как я могу найти этот файл? Он один для одного приложения или их несколько?
Почему путь к user.config такой замороченный?
Можно ли его как-нибудь поменять?
Я развернул мое приложение, используя Clickonce, и сохранил кое-какие настройки, однако не могу найти файл user.config.
Как мои строго типизированные свойства сериализуются в настройки? Я не смог заставить <вставьте ваш тип здесь> сериализоваться корректно.
Моё приложение имеет несколько пользовательских (user scoped) настроек, однако Visual Studio помещает их в app.config. Я думал, они должны быть в файле user.config?
Зачем нужен номер версии в пути к файлу user.config? Из-за него при установке новой версии пользователи теряют все настройки, сохраненные предыдущей версией приложения.
OK, но как мне определить, когда вызывать Upgrade()?
Есть ли способ получить доступ к настройкам из конфигурационного файла, если отсутствует соответствующий класс настроек?
Это довольно мощная возможность. Не является ли она дырой в защите? Кто угодно может получить доступ к моим настройкам без моего ведома и разрешения!
Все это означает, что я не могу получить доступ к настройкам в режиме с частичным доверием?
Вы несколько раз упоминали Configuration API. Что это такое и чем оно отличается от Settings API?
Settings API доступно только в приложениях Windows Forms?

Одна из клевых новых фишек .NET 2.0/VS 2005 – простое в использовании, расширяемое API для управления настройками приложений и их пользователей, т.е. данными, которые должны сохраняться при работе клиентского приложения. Информация на эту тему есть в MSDN и моих предыдущих сообщениях.

Несмотря на то, что новая фича достаточно прозрачна в использовании, мы часто получаем вопросы от пользователей, реализующих собственные сценарии ее использования - почему <это> работает так и как сделать, чтобы <то> работало немного по-другому.

Я заметил, что есть два вида параметров - настройки приложения (Application scoped) и настройки пользователя (User scoped). Причем настройки приложения, кажется только для чтения, и я не могу изменить их во время работы приложения. Почему так?

Есть два основных типа параметров, которые используются приложениями:

  1. Некоторые данные, типа строк подключения к БД или интернет-ссылок, которые изменяются не часто, но, тем не менее, должны быть доступны для редактирования администратором и поэтому не могут быть “зашиты” в код приложения.
  2. Пользовательские настройки и предпочтения, которые могут поменяться в любое время.

Application scoped настройки предназначены для использования в первом сценарии, а User scoped – во втором. Этими соображениями также обусловлено и поведение SettingsProvider по-умолчанию – настройки приложения хранятся в файле app.exe.config рядом с исполняемым файлом, а настройки пользователя – в файле user.config в папке с данными этого пользователя. К app.exe.config файлам вообще может не быть доступа на запись во время работы приложения, например, если приложение установлено в c:\Program Files\ ..., куда имеют доступ на запись только привелигированные пользователи. Даже если такой доступ есть – обычно это не очень хорошая идея, изменять настройки, затрагивающие всех остальных пользователей приложения.

Вы сказали, что файлы user.config находятся в папке с данными пользователя. Как я могу найти этот файл? Он один для одного приложения или их несколько?

Как уже было упомянуто, SettingsProvider по-умолчанию для клиентских приложений (LocalFileSettingsProvider) сохраняет настройки приложения в конфигурационных файлах. В .NET 1.0 и 1.1 было два уровня файлов конфигурации - machine.config и app.exe.config (где ‘app.exe’ – имя файла приложения). В .NET 2.0 мы добавили еще два уровня для хранения настроек, специфичных для пользователя – в папке перемещаемого профиля пользователя и в папке с локальным профилем пользователя. Для XP это будет что-то типа 'c:\Documents and Settings\<username>\Application Data' и 'c:\Documents and Settings\<username>\Local Settings\Application Data' соответственно. Эти каталоги – рекомендованное (а для Windows Logo – требуемое) место для хранения пользовательской информации и большинство приложений (типа Outlook и Visual Studio) помещают пользовательские данные именно здесь.

Точный путь к файлу user.config будет типа такого:

<Profile Directory>\<Company Name>\<App Name>_<Evidence Type>_<Evidence Hash>\<Version>\user.config

где:

Собственно имя файла всегда просто 'user.config'.

Для получения данного пути програмно используйте Configuration Management API (потребуется ссылка на System.Configuration.dll). Например, так можно получить путь к локальному user.config:

   Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
   Console.WriteLine("Local user config path: {0}", config.FilePath);

Почему путь к user.config такой замороченный?

Алгоритм конструирования пути должен обеспечить некоторые строгие требования в плане защиты, изоляции и надежности. Хотя мы постарались сделать этот путь, насколько это возможно, легко воспринимаемым, используя дружественные, определяемые самим приложением строки, не возможно было оставить его совсем уж элементарным, не столкнувшись с коллизиями с другими приложениями, спуфингом и т.д.

Можно ли его как-нибудь поменять?

LocalFileSettingsProvider не предусматривает способа для изменения пути к файлам настроек. Обратите внимание, что собственно провайдер и не определяет путь к файлу настроек в первом случае – это система конфигурации. Если вам нужно хранить настройки, по каким-то соображениям, в другом месте, рекомендованный путь состоит в написании своего SettingsProvider. Его довольно просто реализовать, примеры можно найти в .NET 2.0 SDK. Имейте однако ввиду, что вы можете столкнуться с упомянутыми выше проблемами изоляции.

Я развернул мое приложение, используя Clickonce, и сохранил кое-какие настройки, однако не могу найти файл user.config.

Приведенный выше алгоритм составления пути не используется в случае Clickonce. Вместо этого, локальный файл user.config находится в каталоге с данными Clickonce (при этом часть <Version> в путь также включается). Перемещаемый user.config в случае Clickonce-приложений не применяется.

Как мои строго типизированные свойства сериализуются в настройки? Я не смог заставить <вставьте ваш тип здесь> сериализоваться корректно.

Для сериализации настроек ApplicationSettingsBase может использовать два основных механизма:

  1. Если существует TypeConverter, который может осуществлять преобразования из строки и в строку, используется он.
  2. В противном случае используется XmlSerializer.

Обычно срабатывает один из этих механизмов, однако встречаются типы, для которых эти способы не работают. В этом случае у вас есть следующие варианты:

Моё приложение имеет несколько пользовательских (user scoped) настроек, однако Visual Studio помещает их в app.config. Я думал, они должны быть в файле user.config?

Система конфигурации является иерархической и имеет следующий порядок: машина (machine) -> приложение (application) -> перемещаемый пользователь (roaming user) -> локальный пользователь (local user). Когда вы запрашиваете configuration section на любом уровне, вы получаете слитое представление этой секции с данного уровня и всех уровней ниже (уровень machine – самый нижний, а local user – наивысший). Обработчик секции определяет, как производить слияние, и приоритет настройки, скажем настройка из local user config оказывается приоритетнее, чем из application config.

Таким образом, пользовательские настройки, используемые по-умолчанию, вы можете спутать со значениями из app.config. Когда настройки будут сохранены в user.config, новые значения заменят параметры по-умолчанию. Обратите внимание, что значения по умолчанию могут также быть определены посредством DefaultSettingValueAttribute. Провайдер будет использовать эти значения, если никакие другие значения не определены ни на одном уровне конфигурации.

Зачем нужен номер версии в пути к файлу user.config? Из-за него при установке новой версии пользователи теряют все настройки, сохраненные предыдущей версией приложения.

Есть несколько причин, по которым путь к user.config сделан чуствительным к номеру версии:

  1. Для обеспечения возможности развертывания side-by-side нескольких версий одного приложения (например, если вы используете Clickonce). Разные версии приложения могут использовать различные настройки.
  2. При модернизации приложения класс настроек может быть изменен и станет несовместим с сохраненными ранее настройками, что может привести к проблемам.

Однако мы обеспечили возможность простого переноса настроек предыдущей версии приложения. Просто вызовите ApplicationSettingsBase.Upgrade() и настройки предыдущей версии, которые соответствуют текущей версии класса настроек, будут сохранены в текущей версии user.config. Существует также возможность перегрузки этой функциональности в вашем классе настроек либо в реализации вашего провайдера.

OK, но как мне определить, когда вызывать Upgrade()?

Хороший вопрос. В Clickonce, когда инсталлируется новая версия приложения, ApplicationSettingsBase обнаруживает это и автоматически обновляет настройки. Если Clickonce не используется, вы должны вызвать Upgrade() самостоятельно. Вот один из вариантов, как можно определить, когда нужно вызвать Upgrade():

Заведите в параметрах настройки логическую переменную CallUpgrade и установите для неё значение по-умолчанию в true. При запуске приложения сделайте что-то типа этого:

   if (Properties.Settings.Value.CallUpgrade) {
      Properties.Settings.Value.Upgrade();
      Properties.Settings.Value.CallUpgrade = false;
   }

Такой вариант гарантирует, что Upgrade() будет вызвана только при первом запуске новой версии.

Есть ли способ получить доступ к настройкам из конфигурационного файла, если отсутствует соответствующий класс настроек?

Да, такой способ есть. Например, класс настроек, генерируемый дизайнером VS2005 помечен как internal класс сборки, в которой он определен. Что если необходимо обратиться к некоторым настройкам из другой сборки, скажем из dll, загруженной вашим приложением? Settings API обеспечивает полезный механизм для этого через SettingsGroupNameAttribute. Обычно провайдер настроек использует полное имя вашего класса настроек, как ключ, чтобы изолировать ваши параметры настроек от тех, что используются в других классах. Атрибут SettingsGroupNameAttribute позволяет обращаться к настройкам используя различные ключи или “имена групп”. Так, для доступа к настройкам из класса настроек приложения, все dll должны определить свой собственный класс настроек со свойствами, соответствующими классу настроек приложения, и применить SettingsGroupNameAttribute, назначая имя другого класса как имя группы. Маленькое предупреждение: если вы хотите использовать это в VS, удостоверьтесь, что вы применяете этот атрибут к пользовательской, а не к дизайнерской части partial-класса, т.к. иначе все изменения будут перезаписаны settings-дизайнером.

Это довольно мощная возможность. Не является ли она дырой в защите? Кто угодно может получить доступ к моим настройкам без моего ведома и разрешения!

Это не является брешью в защите по двум причинам:

  1. C точки зрения защиты, изоляция обеспечивается на уровне домена приложения. Рекомендованный в CLR путь заключается в использовании недоверенного (untrusted) кода в отдельном домене приложения. При создании домена приложения вы можете определить для него уникальное дружественное имя и указать его для файла конфигурации приложения. Уникальное дружественное имя гарантирует, что домен приложения получит отдельный файл пользовательских настроек. Таким образом, код из другого домена не сможет получить доступ к вашим настройкам, так как не имеет доступа к вашим файлам конфигурации. И наоборот, любой код, который имеет доступ к вашим файлам конфигурации, может обратиться к параметрам настройки даже без использования SettingsGroupNameAttribute, например, через какое-либо низкоуровневое API (хотя это и требует задания ConfigurationPermission для чтения и немного более высоких для записи).
  2. Если вы уж совсем параноик и не хотите давать доступ к настройкам минуя ваш класс, даже для кода, выполняющегося в том же самом домене, вы можете шифровать настройки перед передачей их провайдеру и расшифровывать при чтении. Settings API не предоставляет для этого специальных способов, однако вы можете использовать Crypto API из .NET Framework. Configuration API также предоставляет возможность шифровать разделы конфигурации – см. эту статью для получения дополнительной информации.

Все это означает, что я не могу получить доступ к настройкам в режиме с частичным доверием?

Нисколько. Проделанная нами работа гарантирует, что вы можете безопасно обратиться к вашим настройкам в режиме с частичным доверием. Фактически это будет выглядеть также как чтение и запись из вашего приложения. Единственное отличие – вы не сможете записать неограниченное количество данных, по понятным причинам. Число байт, которое вы можете записать через Settings API (LocalFileSettingsProvider) при частичном доверии ограничено административно при помощи класса IsolatedStoragePermission. Т.е. собственно также, как и для изолированных хранилищ (Isolated Storage) вообще.

Вы несколько раз упоминали Configuration API. Что это такое и чем оно отличается от Settings API?

Есть фундаментальное отличие между Settings API и Configuration API. Первое обеспечивает объектную модель для управления параметрами приложения. Эта модель использует схему сохранения посредством провайдеров, так что собственно сохранение здесь совершенно абстрактное. То, где хранятся настройки, вопрос реализации провайдера – он может хранить их в raw-файлах, SQL сервере, реестре или вызывать удаленый web-сервис. Провайдер, поставляемый по-умолчанию использует config-файлы, т.к. это самое очевидное хранилище для настроек клиентских приложений. Таким образом, Configuration API – это API более низкого уровня, позволяющее обновлять конфигурационные секции в config-файлах. В некотором смысле Settings API сидит на вершине Configuration API.

Так что если вам нужно работать с настройками приложения и пользовательскими предпочтениями, вам нужно Settings API. Если же вам необходимо реализовать конфигурационные разделы или обращаться к разделам конфигурации напрямую – используйте Configuration API.

Settings API доступно только в приложениях Windows Forms?

Собственно Settings API не имеет никаких ограничений – можно использовать его в любых видах приложений – клиентстких, web, VSTO (Visual Studio Tools for Office Developer), консольных, WPF (Windows Presentation Foundation) и т.д. Провайдер по-умолчанию, LocalFileSettingsProvider, использует config-файлы для хранения настроек, что накладывает определенные ограничения. Например, ASP.NET приложения не имеют user.config файлов, так что вы не сможете сохранять пользовательские настройки используя этот провайдер. Конечно, вы можете использовать Profiles ASP.NET 2.0 для хранения пользовательских настроек. User.config файлы также не поддерживаются для VSTO-приложений (т.е. везде, где основным является native-приложение, например Outlook, Word или IE). В этом случае вы должны будете написать ваш собственный settings-провайдер (что впрочем весьма просто, и существуют хорошие примеры и документация в MSDN на эту тему) для чтения/записи пользовательских настроек. Для основных же типов managed клиентских приложений – консольных, Windows Forms и WPF, LocalFileSettingsProvider полностью поддерживается.


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 1    Оценка 585 [+0/-1]         Оценить