{31 день с Mango} День 30: Локальная база данных

Это День 30 в серии статей 31 день с Mango (перевод оригинальной серии 31 Days of Mango), и был написан приглашенным автором Chris Woodruff. Вы можете найти Chris в твиттере @cwoodruff.
В первоначальной версии Windows Phone 7 мы могли сохранять данные, но потребовалось бы написание собственного кода или использование баз данных сторонних разработчиков, например SterlingDB чтобы иметь реляционное хранилище данных, в котором нуждаются некоторые приложения. Это ограничивает типы приложений, которые могут создавать отдельные разработчики для пользователей.
В Windows Phone Mango разработчики имеют изолированное хранилище для хранения данных и информации в их приложении, но имеют ещё и SQL CE в качестве реляционного хранилища данных, чтобы сделать приложения Windows Phone ещё лучше.
Как и другие решения баз данных для первоначального Windows Phone 7, встроенная база данных SQL CE хранит данные в изолированном хранилище устройства.  Вы можете узнать больше об изолированном хранилище здесь. Кроме того, Microsoft не создавала новых способов работы с данными на телефоне и вместо этого реализовала LINQ to SQL для всех операций с базами данных. LINQ to SQL используется для выполнения всех функций приложения для работы с данными, включая создание данных, заполнение базы данных данными, получение данных, сохранение и удаление данных.
Хорошее руководство по LINQ to SQL находится на MSDN.
Как и во всех отправных точках по созданию приложения для Windows Phone 7, мы начнём с создания проекта Windows Phone Databound Application в Visual Studio 2010.

Мы могли бы начать с создания простого проекта Windows Phone Application, но я нуждаюсь в дополнительных функциях, которые даёт тип проекта с привязкой к данным, чтобы получить возможность использовать в приложении отличный шаблон проектирования MVVM (Model-View-ViewModel).
Далее мы будем обновлять главную страницу (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);

}

Контекст данных (Data Context) является точкой, которая позволяет нам работать с базой данных, а также прокси-классы, которые представляют наши таблицы базы данных. Контекст данных является классом и работает в отношении ряда ”plain old CLR object” (POCO) (примечание: простой CLR объект в старом стиле; CLR — Commom Language Runtime — общеязыковая среда исполнения ) классов, которые мы будем создавать для этого проекта. Табличные объекты, которые представляют наши таблицы базы данных, будут содержать набор объектов для каждой табличной записи, хранящейся в базе данных. Другие подробности о нашей базе также даны нам через контекст данных, таких как таблица ключей и ассоциативные  соответствия между таблицами.
Напомню, что эта локальная база данных не имеет никакой связи с 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 объект. Поскольку эти классы представляют сущности, которые будут храниться в базе данных, назовём их классами Сущности. Для начала класс Сущности должен придерживаться следующих двух интерфейсов:
INotifyPropertyChanged — интерфейс INotifyPropertyChanged используется для уведомления клиентов, как правило привязываемых клиентов, что значение свойства было изменено.
— INotifyPropertyChanging — INotifyPropertyChanging интерфейс используется для уведомления клиентов, как правило привязываемых клиентов, что значение свойства изменяются.
Эти два интерфейса позволят каждой сущности уведомить DataContext, который находится в процессе изменения или изменился. Это будет отражаться внешнем виде нашего приложения описанном в XAML через связывание, которое мы создали.
Класс Сущности должен быть помечен как таблица, чтобы DataContext знал, как работать с ним. Классы Сущности также должны иметь приватные и публичные свойства для каждого свойства Сущности, а также имеющие приватные свойства помеченные, чтобы отдавать значимые метаданные о свойствах Сущности (как столбец базы данных). Помните о необходимости иметь первичный ключ свойств для каждого из классов Сущности.
Ниже приведен класс IdeaItem Сущности, который будет расположен в таблице 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, заключающейся в том, что каждое приложение выполняется изолированно как в «песочнице».

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, свойства observable collection для идей в таблице базы данных и в конструкторе 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.

Наконец мы реализуем сохранение данных об идеях в локальную базу данных. Мы не отправим идеи в базу данных, пока нам нужно повышение производительности. Мы будеи хранить все идеи в локальной observable collection, которую мы создали как свойство 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);
}

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

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

На этом всё! Теперь у вас есть простой способ для создания, хранения и извлечения данных из реляционных баз в ваших приложениях для Windows Phone. Вы уже готовы их использовать?

Вы можете скачать полный проект для Windows Phone, который включает весь код, описанный в этой статье: скачать.

Данный материал является переводом статьи, размещенной на странице: http://www.jeffblankenburg.com/2011/11/30/31-days-of-mango-day-30-local-database/

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *