Статьи

31 Дней Манго | День № 30: Локальная база данных

Эта статья является Днем № 30 из серии под названием « 31 Дней Манго» и была написана приглашенным автором Крисом Вудраффом . С Крисом можно связаться в Твиттере по адресу @cwoodruff .

Day30-LocalDatabase

Что такое локальная база данных?

В исходной версии Windows Phone 7 мы могли сохранять данные, но для создания реляционного хранилища данных, необходимого для некоторых приложений, требовался либо собственный код, либо использование сторонних баз данных, таких как SterlingDB. Это ограничивало типы приложений, которые некоторые разработчики могли бы создавать для пользователей.

В Windows Phone 7 Mango разработчики по-прежнему имеют изолированное хранилище для хранения данных и информации для своих приложений, но теперь используют SQL CE в качестве функции реляционного хранения для создания еще лучших приложений для Windows Phone.

Как и другие решения для баз данных для оригинальной Windows Phone 7, встроенная в Mango база данных SQL CE хранит данные в изолированном хранилище устройства. Вы можете узнать больше о изолированном хранилище здесь . Microsoft также не создала новые способы работы с данными на телефоне и вместо этого внедрила LINQ to SQL для всех операций с базой данных. LINQ to SQL используется для выполнения всех функций приложения при работе с данными, включая создание данных, заполнение данных в базе данных, получение данных и, наконец, сохранение и удаление данных.

Хороший учебник по LINQ to SQL находится здесь, на MSDN.

Настройка локальной базы данных для вашего приложения Mango

Как и все начальные точки для Windows Phone 7, мы начнем с создания проекта приложения Windows Phone Databound Application в Visual Studio 2010.

clip_image002

Мы могли бы начать только с проекта приложения для Windows Phone, но мне нравятся дополнительные функции, которые дает проект Databound, чтобы позволить вашему приложению иметь лучшие шаблоны проектирования, такие как Model-View-ViewModel (MVVM).

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

<phone:PhoneApplicationPage
   x:Class="_31DaysMangoStorage.MainPage"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
   xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
   d:DataContext="{d:DesignData SampleData/MainViewModelSampleData.xaml}"
   FontFamily="{StaticResource PhoneFontFamilyNormal}"
   FontSize="{StaticResource PhoneFontSizeNormal}"
   Foreground="{StaticResource PhoneForegroundBrush}"
   SupportedOrientations="Portrait" Orientation="Portrait"
   shell:SystemTray.IsVisible="True">

    <!--LayoutRoot is the root grid where all page content is placed.-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--TitlePanel contains the name of the application and page title.-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="ApplicationTitle" Text="31 Days of Mango" Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock x:Name="PageTitle" Text="Idea Tracker" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <!--ContentPanel - place additional content here.-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>

            <!-- Bind the list box to the observable collection. -->
            <ListBox x:Name="toDoItemsListBox" ItemsSource="{Binding IdeaItems}"
                    Grid.Row="0" Margin="12, 0, 12, 0" Width="440">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid HorizontalAlignment="Stretch" Width="440">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="50" />
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="100" />
                            </Grid.ColumnDefinitions>
                            <CheckBox
                               IsChecked="{Binding IsComplete, Mode=TwoWay}"
                               Grid.Column="0"
                               VerticalAlignment="Center"/>
                            <TextBlock
                               Text="{Binding ItemName}"
                               FontSize="{StaticResource PhoneFontSizeLarge}"
                               Grid.Column="1"
                               VerticalAlignment="Center"/>
                            <Button
                               Grid.Column="2"
                               x:Name="deleteTaskButton"
                               BorderThickness="0"                                
                               Margin="0"
                               Click="deleteTaskButton_Click">
                                <Image Source="appbar.delete.rest.png"/>
                            </Button>
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>

            <Grid Grid.Row="1">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <TextBox
                   x:Name="newIdeaTextBox"                    
                   Grid.Column="0"
                   Text="add new idea"
                   FontFamily="{StaticResource PhoneFontFamilyLight}"                    
                   GotFocus="newIdeaTextBox_GotFocus"/>
                <Button
                   Content="add"
                   Grid.Column="1"
                   x:Name="newIdeaAddButton"
                   Click="newIdeaAddButton_Click"/>
            </Grid>
        </Grid>
    </Grid>

</phone:PhoneApplicationPage>

Кроме того, чтобы ваше приложение могло компилироваться и выполняться без каких-либо действий, приведенный ниже код будет добавлен в файл MainPage.xaml.cs внутри реализации класса MainPage и после конструктора.

private void newIdeaTextBox_GotFocus(object sender, RoutedEventArgs e)
{
    // Clear the text box when it gets focus.
    newIdeaTextBox.Text = String.Empty;

}

private void newIdeaAddButton_Click(object sender, RoutedEventArgs e)
{

}

protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
    // Call the base method.
    base.OnNavigatedFrom(e);

}

Работа с контекстом данных

Контекст данных — это точка, которая позволит нам работать с базой данных, а также с прокси-классами, представляющими таблицы нашей базы данных. Контекст данных также является классом и работает с рядом классов «простого старого объекта CLR» (POCO), которые мы создадим для этого проекта. Объекты таблиц, которые представляют таблицы нашей базы данных, будут содержать набор сущностей для каждой записи таблицы, хранящейся в базе данных. Другие подробности о нашей базе данных также даются нам через контекст данных, такой как ключи таблиц и сопоставления связей между таблицами.

Напоминаем, что эта локальная база данных не имеет связи с SQL Server 2008 R2, который работает локально на вашем ПК или на сервере вашей компании или хостинг-провайдера и предназначен только для хранения данных на вашем устройстве.

В классе DataContext нет ничего особенного, кроме строки подключения, которую мы знакомы по разработке, и свойств каждой таблицы, которая будет существовать в нашей базе данных. Думайте о DataContext как о «хабе» для данных вашего приложения. Код для DataContext примера приложения ниже.

public class IdeaDataContext : DataContext
{
    // Specify the connection string as a static, used in main page and app.xaml.
    public static string DBConnectionString = "Data Source=isostore:/Ideas.sdf";

    // Pass the connection string to the base class.
    public IdeaDataContext(string connectionString)
        : base(connectionString)
    { }

    // Specify a single table for the to-do items.
    public Table<IdeaItem> IdeaItems;
}

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

Создание базы данных

В отличие от приложений, которые запускаются на вашем компьютере или в IIS 7, базы данных в Windows Phone 7 Mango должны создаваться и инициализироваться при первом запуске приложения на телефоне. Сначала мы рассмотрим создание классов, которые будут представлять наши таблицы базы данных, а затем рассмотрим инициализацию базы данных.

Для каждой таблицы, которую нам нужно построить и показать через базу данных на нашем телефоне для приложения, необходимо создать новый POCO. Поскольку эти классы представляют сущности, которые будут храниться в базе данных, давайте назовем их классами сущностей. Для начала класс Entity должен придерживаться следующих двух интерфейсов:

· INotifyPropertyChanged — интерфейс INotifyPropertyChanged используется для уведомления клиентов, как правило, для привязки клиентов к изменению значения свойства.

· INotifyPropertyChanging — интерфейс INotifyPropertyChanging используется для уведомления клиентов, обычно связывающих клиентов об изменении значения свойства.

Эти два интерфейса позволят каждому объекту уведомлять DataContext, который находится в процессе изменения или изменился. Затем это будет отражено в представлениях XAML нашего приложения через установленную нами привязку.

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

Ниже приведен класс IdeaItem Entity, который будет расположен в таблице IdeaItems, на которую ссылается DataContext, который мы создали ранее.

[Table]
public class IdeaItem : INotifyPropertyChanged, INotifyPropertyChanging
{
    private int _ideaItemId;
    [Column(IsPrimaryKey = true, IsDbGenerated = true, DbType = "INT NOT NULL Identity", CanBeNull = false, AutoSync = AutoSync.OnInsert)]
    public int IdeaItemId
    {
        get
        {
            return _ideaItemId;
        }
        set
        {
            if (_ideaItemId != value)
            {
                NotifyPropertyChanging("IdeaItemId");
                _ideaItemId = value;
                NotifyPropertyChanged("IdeaItemId");
            }
        }
    }

    private string _itemName;
    [Column]
    public string ItemName
    {
        get
        {
            return _itemName;
        }
        set
        {
            if (_itemName != value)
            {
                NotifyPropertyChanging("ItemName");
                _itemName = value;
                NotifyPropertyChanged("ItemName");
            }
        }
    }

    private bool _isComplete;
    [Column]
    public bool IsComplete
    {
        get
        {
            return _isComplete;
        }
        set
        {
            if (_isComplete != value)
            {
                NotifyPropertyChanging("IsComplete");
                _isComplete = value;
                NotifyPropertyChanged("IsComplete");
            }
        }
    }

    [Column(IsVersion = true)]
    private Binary _version;
    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangingEventHandler PropertyChanging;
    private void NotifyPropertyChanging(string propertyName)
    {
        if (PropertyChanging != null)
        {
            PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
        }
    }
}

Наконец, мы должны создать базу данных, если она не существует. Это будет сделано в конструкторе класса Application. Вы найдете это в файле App.xaml.cs. Код, который будет добавлен в конец метода конструктора, выглядит следующим образом.

using (IdeaDataContext db = new IdeaDataContext(IdeaDataContext.DBConnectionString))
{
    if (db.DatabaseExists() == false)
    {
        db.CreateDatabase();
    }
}

Теперь у нас есть база данных, которая была создана и инициализирована в изолированном хранилище приложения. Еще одна вещь, которую нужно помнить, это то, что локальные базы данных в Windows Phone 7 Mango не могут быть разделены напрямую между приложениями на телефоне из-за безопасности изолированной среды, установленной в операционной системе Windows Phone 7 Mango.

Поддержка LINQ to SQL для Windows Phone Mango

Windows Phone 7.1 SDK позволяет использовать некоторые, но не все функции LINQ to SQL в Windows Phone. Ниже приведены лишь некоторые вещи, которые следует помнить при работе с данными и LINQ to SQL в Windows Phone 7 Mango.

· ExecuteCommand не поддерживается

· Объекты ADO.NET (такие как DataReader) не поддерживаются

· Поддерживаются только типы данных Microsoft SQL Server Compact Edition (SQL CE).

Чтобы получить больше ограничений и возможностей использования LINQ to SQL в Mango, прочитайте страницу MSDN здесь .

Получение данных из локальной базы данных

Чтобы получить данные или любую связанную с данными работу с локальной базой данных, мы должны сначала создать DataContext и подключиться к базе данных. Это произойдет в MainPage.xaml.cs через закрытую переменную для DataContext, наблюдаемое свойство коллекции для идей в таблице базы данных и в конструкторе MainPage, как показано ниже.

private IdeaDataContext ideaDB;

private ObservableCollection<IdeaItem> _ideaItems;
public ObservableCollection<IdeaItem> IdeaItems
{
    get
    {
        return _ideaItems;
    }
    set
    {
        if (_ideaItems != value)
        {
            _ideaItems = value;
            NotifyPropertyChanged("IdeaItems");
        }
    }
}

public MainPage()
{
    InitializeComponent();

    ideaDB = new IdeaDataContext(IdeaDataContext.DBConnectionString);
    this.DataContext = this;
}

Чтобы наши идеи находились в локальной базе данных, мы будем использовать LINQ to SQL для запроса и возврата коллекции из базы данных через DataContext.

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
    var ideaItemsInDB = from IdeaItem todo in ideaDB.IdeaItems
                        select todo;

    IdeaItems = new ObservableCollection<IdeaItem>(ideaItemsInDB);
    base.OnNavigatedTo(e);
}

Идеи теперь связаны и отражены в MainPage.xaml.

clip_image003

Хранение данных в локальной базе данных

Наконец, мы создадим сохранение данных нашей идеи в локальной базе данных. Мы не будем отправлять идеи в базу данных, пока нам не понадобится повысить производительность. Мы будем хранить все идеи в локальной наблюдаемой коллекции, которую мы создали как свойство MainPage (IdeaItems). Добавление новых идей в локальную базу данных будет происходить по нажатию кнопки, когда новая идея добавляется в коллекцию IdeaItems.

private void newIdeaAddButton_Click(object sender, RoutedEventArgs e)
{
    IdeaItem newIdea = new IdeaItem { ItemName = newIdeaTextBox.Text };

    IdeaItems.Add(newIdea);
    ideaDB.IdeaItems.InsertOnSubmit(newIdea);
}

Как упоминалось ранее, идеи, собранные от пользователя, не будут храниться в базе данных до тех пор, пока приложение не перейдет с главной страницы, либо когда пользователь выйдет из приложения или приложение переместится на новую страницу в приложении. Код для события OnNavigateFrom для MainPage приведен ниже.

protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
    base.OnNavigatedFrom(e);
    ideaDB.SubmitChanges();
} 

Резюме

Вот и все! Теперь у вас есть простой способ создавать, хранить и извлекать реляционные данные в приложениях Windows Phone. Как вы собираетесь использовать это?

Чтобы загрузить полный проект Windows Phone, включающий весь код и концепции из этой статьи, нажмите кнопку «Загрузить код» ниже.

скачать

Завтра в заключительной статье этой серии мы расскажем о лучших методах продвижения ваших приложений для Windows Phone. Увидимся позже!

toolsbutton

Источник:  http://www.jeffblankenburg.com/2011/11/30/31-days-of-mango-day-30-local-database