Дата публикации статьи: 18.05.2006 09:53

Работа с изолированным хранилищем в .NET

Автор: David Wasserman
[Оригинал статьи] [Обсудить в форуме]
Перевод с английского: Виталий Готовцов
WWW: http://www.vitgot.narod.ru

Скачать исходный код к статье

Введение

Среди огромного множества классов и функций, предлагаемых Microsoft .NET Framework, существует новая и мощная функция для сохранения информации, относящейся к пользователям и приложениям, которая называется Изолированным хранилищем. Пространство имен System.IO.IsolatedStorage позволяет записывать данные на жесткий диск, что раньше, иногда, было невозможно. Благодаря этому появилась возможность сохранять такую информацию, как состояние приложения или предпочтения пользователя.
    И ASP.NET и приложения Windows Form могут использовать все возможности изолированного хранения. Приложения ASP.NET могут использовать его для хранения персонализированной информации, которая полезна для настройки сайта для каждого пользователя. Приложения Windows традиционно помещают данные приложений в .INI файлы и настройки системного реестра, но разработчики могут теперь также использовать изолированное хранение.
    Эта статья предлагает два примера Windows-приложений, информация которых может быть применена с изолированным хранилищем при использовании пространств имен ADO.NET и XML. Данные легко читаются и записываются с помощью IO-потоков и изолированного хранения.

Как работает изолированное хранение?

Изолированное хранение помогает управлять чтением и записью данных в определенную область хранения. Вы можете относиться к ней, как к области типа «песочницы» [«sandbox» - механизм защиты виртуальной java-машины, который заключается в исполнении апплетов в изолированной среде (прим. переводчика)] на жестком диске, в которую могут записывать данные разные приложения, включая те, которые в обычных условиях не авторизованы и не могут этого делать. Оно разделяет все уровни доверия кода, написанного в .NET Framework, от неблагонадежных, до тех, которые обладают полным доверием.
Существуют два типа изоляции, которые могут быть использованы:

  1. Изоляция пользователя и сборки
  2. Изоляция пользователя, домена и сборки

Оба типа изоляции требуют, чтобы область хранения была связана с пользователем и сборкой. Только пользователь и сборка, создавшие хранилище, имеют к нему доступ.
    Изоляция пользователя и сборки: Информация, помещенная с этим типом изоляции, может быть доступна многим приложениям с соответствующими уровнями доверия. Например, интранет-сайт может иметь доступ к этой информации, а интернет-сайт – не может по умолчанию.
    Изоляция пользователя, домена и сборки: Этот тип изоляции имеет те же требования, что и первый тип для пользователя и сборки, и добавляет доменное приложение перед ними, если оно есть. С доменным приложением (или, короче, доменом) изоляция становится даже более гранулированным. Теперь только код, создавший хранилище, имеет доступ к нему и это должна быть сборка, запущенная из того же приложения. Никакое другое приложение не может иметь доступ к хранилищу. При этом создается дополнительный слой безопасности, который является необходимым, если код запущен из неблагонадежных источников, таким, как интернет.

Какой тип изоляции использовать?

Используйте хранилище «пользователь и сборка», если запускаете приложения из доверенных источников, которые требуют разделения информации среди многих приложений для этой сборки. Используйте тип «пользователь, сборка и домен», если запускаете приложения из ненадежных источников, например, компоненты, скачанные из интернета. Также вы можете использовать этот тип хранилища, если необходимо, чтобы каждое приложение имело свое отдельно локализованное хранилище.

Пример 1: Создание XML-файлов и изолированным хранилищем

Общим случаем применения изолированного хранения является Windows-приложение, которое сохраняет пользовательские настройки. Чтобы продемонстрировать, как это делать, я создал XML-файл, который содержит цвет фона окна и размер, и свойство text элемента управления textbox (текстовое поле). Попробуйте сами создать Visual Basic.NET Windows-приложение в Visual Basic.NET. Форма будет содержать три кнопки, текстовое поле и диалоговое окно ColorDialog. Добавьте элементы управления в форму со следующими настройками:

Таблица 1

Тип Название Текст
Button btnBackColor Change Background Color
Button btnSave Save Settings
Button btnDelete Delete Settings
TextBox txtSampleData sample data in here
ColorDialog cdBackground  

Щелкните на кнопке View Code в проводнике решений и добавьте следующее в первой строке кода:

Imports System
Imports System.IO
Imports System.IO.IsolatedStorage
Imports System.Text
Imports System.Xml

При этом импортируются необходимые имена пространств, которые требуются для этой программы.
    Добавьте следующие строки кода в событие click btnBlackColor:

If cdBackground.ShowDialog() = DialogResult.OK Then
    Me.BackColor = cdBackground.Color
End If

Это стандартный код для обработки результата, полученного из элемента ColorDialog.
    Сначала я опишу, как сохранить файл с изолированным хранилищем. Позже, в этом примере, я покажу, как вернуть файл, который уже был сохранен с изолированным хранилищем. В отношении изолированного хранения первой задачей является создание необходимого объекта для работы с файлом хранилища. Объект IsolatedStorageFile создан. Я выбрал изоляцию типа «пользователь, домен и сборка» для использования. Я не собираюсь разделять эту область хранения с другими приложениями, так что этот тип изоляции подходит для моих нужд. Вторая строка кода устанавливает тип изоляции с опцией GetUserStoreForDomain. Если бы я хотел использовать изоляцию типа «пользователь и сборка» вместо этого, мне бы просто пришлось заменить ее на GetUserStoreForAssembly.
    Следующие сегменты кода помещены в событие click btnSave:

Dim isoStorage As IsolatedStorageFile
isoStorage = IsolatedStorageFile.GetUserStoreForDomain

Следующая строка кода создает объект IsolatedStorageFileStream. Первый параметр – это путь и название XML-файла для записи. Второй параметр содержит FileMode с которым откроется файл. В данном случае новый файл будет создан и переписан, если он уже существует. И, наконец, последний параметр - объект IsolatedStorageFile, в этом случае isoStorage.

Dim stmWriter As New IsolatedStorageFileStream ("UserSettingsXML.xml", _
    FileMode.Create, isoStorage) 

С началом записи XML-файла будет создан и ассоциирован с объектом потока объект XmlTextWriter.

Dim writer As New XmlTextWriter(stmWriter, Encoding.UTF8)

Следующие строки кода просто записывают XML-файл по одной строке:

writer.Formatting = Formatting.Indented
writer.WriteStartDocument()
writer.WriteStartElement("UserSettings")
writer.WriteStartElement("BackColor")
writer.WriteString(Me.BackColor.ToArgb.ToString)
writer.WriteEndElement()
writer.WriteStartElement("Width")
writer.WriteString(Me.Width.ToString)
writer.WriteEndElement()
writer.WriteStartElement("Height")
writer.WriteString(Me.Height)
writer.WriteEndElement()
writer.WriteStartElement("SampleData")
writer.WriteString(txtSampleData.Text)
writer.WriteEndElement()
writer.WriteEndElement()

Удаление и закрытие записывающего объекта завершит запись XML-файла. Считается хорошей практикой закрывать файл хранилища и объект потока. Это позволяет позже обрабатывать файл изолированного хранилища без учета ошибок.

writer.Flush()
writer.Close()

stmWriter.Close()
isoStorage.Close()

Вот и все, что касается создания XML-файла с помощью изолированного хранения. Но что произойдет, когда приложение будет запущено в следующий раз? После того, как настройки были сохранены, нужно, чтобы был способ прочесть и восстановить их. Лучшим местом для этого является событие формы Load.
    Снова, как в событии click btnSave, мы объявляем и инициализируем объект IsolatedStorageFile.

Dim isoStorage As IsolatedStorageFile
isoStorage = IsolatedStorageFile.GetUserStoreForDomain

Следующие несколько строк определяют, существует ли XML-файл с настройками:

Dim StoreFileNames As String()
Dim StoreFile As String
StoreFileNames = isoStorage.GetFileNames("UserSettingsXML.xml")

For Each StoreFile In StoreFileNames
    If StoreFile = "UserSettingsXML.xml" Then

Если файл существует, то происходит чтение XML-файла в объект, читающий поток:

        Dim stmReader As New StreamReader(New IsolatedStorageFileStream( _
            "UserSettingsXML.xml", FileMode.Open, isoStorage))
        Dim xmlReader As New XmlTextReader(stmReader)

При использовании объекта xmlReader файл будет считываться по одной строке, сравнивая каждый узел с соответствующим значением настройки. Когда находится подходящее значение, оно считывается и обрабатывается для изменения ассоциированного с ним значения формы:

        While xmlReader.Read()
            Select Case xmlReader.Name
                Case "BackColor"
                    Me.BackColor = Color.FromArgb(Integer.Parse( _
                        xmlReader.ReadString))
                Case "Width"
                    Me.Width = Integer.Parse(xmlReader.ReadString)
                Case "Height"
                    Me.Height = Integer.Parse(xmlReader.ReadString)
                Case "SampleData"
                    txtSampleData.Text = xmlReader.ReadString
            End Select
        End While

И снова в событии click btnSave мы очищаем и закрываем объекты обработчика:

        xmlReader.Close()
        stmReader.Close()

Закройте цикл поиска в файле и объект IsolatedStorageFile:

    End If
Next

isoStorage.Close()

Наконец, существует ещё один элемент управления на форме, кнопка btnDelete. Эта кнопка служит для удаления XML-файла с настройками и восстановления исходных свойств формы. Весь этот фокус сделают четыре строки. Первые две мы уже видели: объявление и достижение объекта хранилища. Третья собственно и выполняет удаление. Значением передаваемого параметра является файл, который нужно удалить. Последняя, четвертая, строка закрывает объект IsolatedStorageFile.
    Следующий сегмент кода помещен в событие Click btnDelete:

Dim isoStorage As IsolatedStorageFile
isoStorage = IsolatedStorageFile.GetUserStoreForDomain
isoStorage.DeleteFile("UserSettingsXML.xml")
isoStorage.Close()

Обратите внимание на то, что если файл не найден, происходит исключение. Образец кода, который прилагается к этой статье, содержит исключение и обработку ошибки. Для краткости статьи он не включен в обсуждение.
    Теперь настало время проверить этот код. Попробуйте запустить программу, изменяя цвет фона, текстовое поле, изменяя размер формы. Затем щелкните на кнопке Save Settings. Закройте приложение и снова запустите. Оно откроется с сохраненными настройками. Теперь попробуйте удалить настройки и снова закройте приложение. Когда вы запустите его в следующий раз, будут использованы исходные настройки формы.

Пример 2: Создание ADO.NET DataSet с изолированным хранилищем

Это второй пример основан на той же концепции, что и первый пример и будет действовать таким же образом. Отличие этого примера реализовано в мощных возможностях ADO.NET создавать файл настроек вместо сырого XML в первом примере.
    Создайте Windows-приложение такое же, как в первом примере. Добавьте те же элементы управления, что и в Таблице 1.
    Щелкните кнопку View Code в проводнике решений и добавьте следующее в первой строке кода:

Imports System
Imports System.IO
Imports System.IO.IsolatedStorage

При этом импортируются необходимые пространства имен, которые требуются для этой программы.
    Сразу после определения класса добавьте код, создающий на уровне класса объекты DataSet и DataTable. Эти объекты будут использоваться во всем коде для сохранения данных в изолированном хранилище и извлечения данных из него.

Dim dsSettings As DataSet = New DataSet("Settings")
Dim tblSettings As DataTable = dsSettings.Tables.Add("userSettings")

Добавьте следующие строки в событие click btnBackColor:

If cdBackground.ShowDialog() = DialogResult.OK Then
    Me.BackColor = cdBackground.Color
End If

Это обычный код для обработки элемента ColorDialog.
    В этом примере имеет смысл объяснить, почему сначала загружается файл настроек, прежде чем сохраненный. Это сделает представленную идею легче для понимания. Это приводит нас к событию Form Load. Первое, о чем приходится заботиться, это создание столбцов в таблице ADO.NET, которые будут содержать значения настроек.
    Следующие сегменты кода необходимо поместить в событие Load формы:

tblSettings.Columns.Add("BackColor", Type.GetType("System.Int32"))
tblSettings.Columns.Add("Width", Type.GetType("System.Int32"))
tblSettings.Columns.Add("Height", Type.GetType("System.Int32"))
tblSettings.Columns.Add("SampleData", Type.GetType("System.String"))

Затем необходимо создать объект IsolatedStorageFile. Как показано в Примере 1, эти две строки кода необходимы для работы с файлом хранилища:

Dim isoStorage As IsolatedStorageFile
isoStorage = IsolatedStorageFile.GetUserStoreForDomain

Следующие две строки определяют, существует ли уже файл с настройками. Только если файл существует, произойдет попытка прочитать файл с настройками пользователя:

Dim StoreFileNames As String()
Dim StoreFile As String
StoreFileNames = isoStorage.GetFileNames("UserSettingsDS.xml")

For Each StoreFile In StoreFileNames
    If StoreFile = "UserSettingsDS.xml" Then

Если файл хранилища найден, создается StreamReader и файл с пользовательскими настройками обрабатывается. Создается IsolatedStorageFileStream как StreamReader. Этот файл затем считывается на уровне класса в DataSet, объявленный в начале кода. После этого закрывается объект StreamReader.

        Dim stmReader As New StreamReader(New IsolatedStorageFileStream( _
            "UserSettingsDS.xml", FileMode.Open, isoStorage))
        dsSettings.ReadXml(stmReader, XmlReadMode.ReadSchema)
        stmReader.Close()

После того, как загружен DataSet, таблица считывается по одной строке (в данном случае существует только одна строка). Когда каждая строка прочитана, файл настроек используется для изменения формы и преобразования значений к соответствующим типам данных.

        Dim dr As DataRow
        For Each dr In dsSettings.Tables(0).Rows
            Me.BackColor = Color.FromArgb(Integer.Parse(dr("BackColor")))
            Me.Width = Integer.Parse(dr("Width"))
            Me.Height = Integer.Parse(dr("Height"))
            txtSampleData.Text = dr("SampleData")
        Next

Закройте цикл поиска в файле и объект IsolatedStorageFile:

    End If
Next

isoStorage.Close()

Подумайте, зачем нужна кнопка btnSave? Вы правы, она нужна для сохранения пользовательских настроек в изолированное хранилище. Первым делом нужно очистить DataTable.
Следующие сегменты кода нужно поместить в событие Click btnSave:

tblSettings.Clear()

Создайте новый DataRow и поместите текущие настройки формы в значения столбцов в строке. Затем добавьте этот DataRow в DataTable.

Dim drSettings As DataRow
drSettings = tblSettings.NewRow

drSettings("BackColor") = Me.BackColor.ToArgb
drSettings("Width") = Me.Width
drSettings("Height") = Me.Height
drSettings("SampleData") = txtSampleData.Text

tblSettings.Rows.Add(drSettings)

Снова мы создаем объект IsolatedStorageFile:

Dim isoStorage As IsolatedStorageFile
isoStorage = IsolatedStorageFile.GetUserStoreForDomain

Затем происходит запись файла для изолированного хранения. Это вызывает создание объекта StreamWriter, а затем сохранение DataSet как XML-файла:

Dim stmWriter As New StreamWriter(New IsolatedStorageFileStream( _
    "UserSettingsDS.xml", FileMode.Create, isoStorage))
dsSettings.WriteXml(stmWriter, XmlWriteMode.WriteSchema)

После этого считается хорошей практикой закрыть эти объекты:

stmWriter.Close()
isoStorage.Close()

Последнее, что необходимо сделать, это добавить код для кнопки btnDelete. Этот код идентичен коду в Примере 1, единственным отличием будет название файла:

Dim isoStorage As IsolatedStorageFile
isoStorage = IsolatedStorageFile.GetUserStoreForDomain
isoStorage.DeleteFile("UserSettingsDS.xml")
isoStorage.Close()

Вот и все! Вы все сделали, и можете провести тестирование, как предлагалось в Примере 1.

Заключение

Эта статья предлагает короткое введение в то, как работает изолированное хранение в .NET Framework. Два примера показывают, как использовать изолированное хранение с XML и ADO.NET пространствами имен. Она предлагает легкий, удобный и непротиворечивый способ сохранения информации на клиентских машинах, что сделать иным способом будет трудно. Смотрите исходные коды к статье с полным листингом кода, представленного в этой статье.