Хранение данных в Windows Phone 7 средствами Isolated Storage

Вопрос хранения данных затрагивает практически любое приложения и не может оставаться в стороне. При разработке приложений для Windows Phone 7 в качестве основного сценария на данный момент предлагается использовать механизм Isolated Storage, который успешно используется в приложениях Silverlight. Давайте попробуем поработать с Isolated Storage.

Isolated Storage предполагает, что для каждого приложения существует изолированная среда для хранения данных.Приложения не могут получать доступ к хранилищу других приложений (в основном из соображений безопасности). Само хранилище при этом разделяется на две части:

  • Isolated Settings Storage;
  • Isolated File Storage.

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

Хранилище Isolated File Storage предназначено для хранения различных данных в виде файлов. При этом это хранилище можно структурировать, используя разбиение на папки. Фактически, это – некоторый доступ к файловой системе изолированного хранилища.

Для организации механизма хранения данных в Windows Phone 7 SDK используется следующий набор объектов:

  • System.IO.IsolatedStorage.IsolatedStorageSettings – предоставляет доступ к словарю (ключ-значение) настроек для текущего хранилища (Isolated Settings Storage);
  • System.IO.IsolatedStorage.IsolatedStorageFile – позволяет получить доступ к хранилищу данных (Isolated File Storage);
  • System.IO.IsolatedStorage.IsolatedFileStream – позволяет получить доступ к содержимому файла в хранилище (Isolated File Storage);
  • System.IO.IsolatedStorage.IsolatedStorageException – исключение, возникающее при ошибках в работе с хранилищем.

Хранение настроек

Для доступа к настройкам приложения используется класс IsolatedStorageSettings. Этот класс представляет собой словарь, при помощи которого по ключу можно обратиться к конкретной настройке. Для доступа к значению используется индексатор. Например, получить доступ к ключу count можно используя следующий код.

int count = (int)System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings["count"];

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

int count = 0;

if (System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings.Contains("count") == true)
{
    count = (int)System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings["count"];
}

Аналогичным образом можно работать с любыми ключами Isolated Settings Storage.

Изменение настроек приложения также выполняется достаточно просто. Для этого следует изменить нужный ключ и вызвать метод Save.

System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings["count"] = count;
System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings.Save();

Так, например, можно написать несложный код по подсчету количества запусков приложения и отображения этой информации пользователю. Для этого добавим на форму элемент TextBlock и будем инкрементировать счетчик при каждом запуске приложения. Эту логику можно поместить, например, в конструктор.

public MainPage()
{
    InitializeComponent();

    SupportedOrientations = SupportedPageOrientation.Portrait | SupportedPageOrientation.Landscape;

    int count = 0;
    if (System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings.Contains("count") == true)
    {
        count = (int)System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings["count"];
    }
    
    count++;
    System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings["count"] = count;
    System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings.Save();

    textBlock1.Text = count.ToString();
}

На самом деле, работать с настройками в таком виде не очень удобно – каждый раз приходится проверять наличие ключа в настройках и выполнять приведение типов. По-хорошему, тут еще нужно проверять на соответствие типов перед выполнением приведения типов. Поэтому давайте создадим небольшую надстройку для класса IsolatedStorageSettings.

Надстройка будет представлять с собой несколько методов-расширений для класса IsolatedStorageSettings. Для получения настроек в типизированном виде создадим метод GetSetting, который будет принимать в качестве параметра имя ключа и значение по умолчанию (если, например, ключ не найден). Также сделаем перегруженную версию метода, чтобы можно было не указывать значение по умолчанию. Внутри этого метода выполним все действия, которые мы видели с вами раньше.

public static class IsolatedStorageSettingsExtensions
{
    public static T GetSetting<T>(this System.IO.IsolatedStorage.IsolatedStorageSettings settings, string keyName)
    {
        return GetSetting(settings, keyName, default(T));
    }

    public static T GetSetting<T>(this System.IO.IsolatedStorage.IsolatedStorageSettings settings, string keyName, T defaultValue)
    {
        if (settings != null)
        {
            if (settings.Contains(keyName) == true)
            {
                try
                {
                    return (T)settings[keyName];
                }
                catch (InvalidCastException)
                {
                }
            }
        }

        return defaultValue;
    }
}

Теперь доступ к настройкам немного упростился. Для того, чтобы получить значение, можно просто вызвать метод-расширение с нужными параметрами. Перепишем с учетом этого, код, который использовался выше.

public MainPage()
{
    InitializeComponent();

    SupportedOrientations = SupportedPageOrientation.Portrait | SupportedPageOrientation.Landscape;

    int count = System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings.GetSetting<int>("count");
    count++;
    System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings["count"] = count;
    System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings.Save();

    textBlock1.Text = count.ToString();
}

Мы также можем указать значение по умолчанию, которое будет использоваться, если ключ не найден в настройках. Например, мы можем вести отсчет с числа 10.

int count = System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings.GetSetting<int>("count", 10);

Аналогичный метод расширения сделаем и для сохранения настроек. Тогда весь класс, содержащий методы-расширения будет выглядеть следующим образом.

public static class IsolatedStorageSettingsExtensions
{
    public static T GetSetting<T>(this System.IO.IsolatedStorage.IsolatedStorageSettings settings, string keyName)
    {
        return GetSetting(settings, keyName, default(T));
    }

    public static T GetSetting<T>(this System.IO.IsolatedStorage.IsolatedStorageSettings settings, string keyName, T defaultValue)
    {
        if (settings != null)
        {
            if (settings.Contains(keyName) == true)
            {
                try
                {
                    return (T)settings[keyName];
                }
                catch (InvalidCastException)
                {
                }
            }
        }

        return defaultValue;
    }

    public static void SaveSetting<T>(this System.IO.IsolatedStorage.IsolatedStorageSettings settings, string keyName, T value)
    {
        if (settings != null)
        {
            settings[keyName] = value;
            settings.Save();
        }
    }
}

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

public MainPage()
{
    InitializeComponent();

    SupportedOrientations = SupportedPageOrientation.Portrait | SupportedPageOrientation.Landscape;

    int count = System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings.GetSetting<int>("count", 10);
    System.IO.IsolatedStorage.IsolatedStorageSettings.ApplicationSettings.SaveSetting("count", ++count);
    textBlock1.Text = count.ToString();
}

Хранение данных

Для доступа к файловому хранилищу используется класс IsolatedStorageFile. Обратившись к его статическому методу GetUserStoreForApplication() можно получить доступ к объекту, предоставляющему всевозможные методы для работы с файлами и папками:

  • GetDirectoryNames – позволяет получить список всех папок в заданном контексте;
  • GetFileNames – позволяет получить список всех файлов в заданном контексте;
  • CreateDirectory – позволяет создать папку;
  • DeleteDirectory – позволяет удалить папку;
  • DirectoryExists – позволяет проверить наличие папки;
  • CreateFile – позволяет создать файл;
  • OpenFile – позволяет открыть файл для чтения и/или изменения;
  • DeleteFile – позволяет удалить файл;
  • FileExists – позволяет проверить наличие файла.

Как видно, для работы с файлом используется метод OpenFile. Этот файл возвращает поток, с которым можно работать так, как мы это делаем обычно (например, используя StreamReader/StreamWriter). Например, код для записи информации в файл будет выглядеть следующим образом.

using (var file = IsolatedStorageFile.GetUserStoreForApplication().CreateFile("test1.txt"))
{
    using (var fileWriter = new StreamWriter(file))
    {
        fileWriter.WriteLine(DateTime.Now.ToString());
    }
}

Аналогичным образом выглядит код для считывания содержимого файла.

using (var file = IsolatedStorageFile.GetUserStoreForApplication().OpenFile("test1.txt", FileMode.Open))
{
    using (var fileReader = new StreamReader(file))
    {
        MessageBox.Show(fileReader.ReadToEnd(), "File", MessageBoxButton.OK);
    }
}

Давайте добавим в конструктор нашего приложения логику, согласно которой приложение будет проверять наличие файла в хранилище и, если его там нет, записывать в него текущее время; в противном случае – отображать его пользователю.

public MainPage()
{
    // ...

    if (IsolatedStorageFile.GetUserStoreForApplication().FileExists("test1.txt") == false)
    {
        using (var file = IsolatedStorageFile.GetUserStoreForApplication().CreateFile("test1.txt"))
        {
            using (var fileWriter = new StreamWriter(file))
            {
                fileWriter.WriteLine(DateTime.Now.ToString());
            }
        }
    }
    else
    {
        using (var file = IsolatedStorageFile.GetUserStoreForApplication().OpenFile("test1.txt", FileMode.Open))
        {
            using (var fileReader = new StreamReader(file))
            {
                MessageBox.Show(fileReader.ReadToEnd(), "File", MessageBoxButton.OK);
            }
        }
    }
}

Таким образом, без особых усилий мы создали небольшое приложение, работающее с Isolated Storage - Isolated Settings Storage и Isolated File Storage. Как видно, на это не требуется много усилий, а работа с хранилищем очень похожа на аналогичные операции в настольном Silverilght. Тем не менее, по сравнению с хранилищем в Silverilght, хранилище в Windows Phone не имеет ограничений, связанных с объемом хранимой информации.