XNA и базы данных с использованием Entity Framework

XNA и базы данных с использованием Entity Framework

В этой статье в бы хотел немного рассказать о том как можно работать с базами данных в XNA Framework. Дело в том, что при программировании под Windows (под Xbox это работать не должно, к сожалению) в нашем распоряжении оказывается вся инфраструктура .NET Framework, в частности различные технологии, отвечающие за работу с базами данных.


Сейчас мы рассмотрим работу с Entity Framework для загрузки данных об игре из базы данных. Но сначала я бы хотел предупредить, что запрос к базе данных – крайне ресурсоемкая операция (для игры), так что, желательно, загрузить все что нужно при инициализации игры, нового уровня и т.д.. А вот на каждом кадре делать этого не стоит.

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

Я буду использовать Entity Framework и, если я не ошибаюсь, то для его поддержки нужно устаносить пакет обновлений 1 для Visual Studio 2008.

Вернемся к проекту, в котором уже реализована модель освещения Блинна-Фонга и для начала добавим в него базу данных.

Для этого создаем папку Data (название, безусловно, может быть другим) и в ней жмем Add->New Item->Date->Local Database

Я назвал базу данных GameDB.sdf, жмем Add


Теперь нужно добавить таблицы. Для этого переходим к окну Server Explorer (в меню View). В Database Connections находим нашу базу данных, разпахиваем в ней папку Tables. В контекстном меню выбираем CreateTable.

Самое время точно определиться с тем, что именно мы хотим хранить в базе данных и со структурой таблиц.

Для каждого примитива будем хранить :

  • Позицию
  • Тип

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

Следуя правилам нормализации таблиц, нам придется создать две таблицы:

Primitives – эта таблица будет создержать координаты примитива и ссылку на его тип.

PrimitiveTypes – эта таблица будет содержать все возможные типы примитивов.

Заполним сначала таблицу PrimitiveTypes, нужно ввести название таблицы, название и типы колонок.

Колонка ID – это первичный ключ таблицы, создается он для того, чтобы каждая запись совершенно точно была уникальное. Это обязательное требование для Entity Framework. Чтобы не изобретать ничего нового, проще всего добавлять в каждую таблицу колонку ID, типа int. Для нее установить параметры как указано на картинке:

AllowNulls – No

Unique – Yes

Primary Key – Yes

Также в нижнем окне нужно установить Identity в значение True, в таком случае значение этого столбца будет всегда заполняться автоматически при вставке новых данных.


По аналогии создаем вторую таблицу Primitives.

Колонка ID – первичный ключ (его параметры задаются как в прошлой таблице).

X,Y,Z – позиция примитива (они будут иметь тип float, хотя он не соответствует float в c#, но сейчас это не слишком важно)

TypeID – это и будет ссылка на тип примитива. Пока установим для нее тип int и Allow Nulls = No (примитив не может не иметь типа)


Теперь нужно добавить саму связь. Для этого из контекстного меню Primitives выбираем Table Properties. В меню Add Relations вводим значения с картинки:


Relation Name – не важно, я ввел PR0

Primary Key Table – PrimitiveTypes (именно там находятся данные, на которые мы будем ссылаться из таблицы Primitives)

Primary Key Table Column – ID

Foreign Key Table – Primitives

Foreign Key Table Column – TypeID

Жмем AddColumns, затем Add Relation. Если все сделано правильно, то появится сообщение:

Constraint Successfully Added.

Структука базы готова, тепепь можно заполнить ее данными.

Это можно сделать несколькими способами:

Из контекстного меню таблицы выбрать Show Table Data и добавить нужные строки

Или выбрать в контекстном меню New Query и, в появившемся поле, ввести запросы на добавление данные.

Сейчас мне удобнее выпонить это вторым способом, текст запроса будем следующим:

INSERT INTO PrimitiveTypes(Name) VALUES (‘Teapot’);

INSERT INTO PrimitiveTypes(Name) VALUES (‘Cube’);

INSERT INTO PrimitiveTypes(Name) VALUES (‘Torus’);

INSERT INTO Primitives(X,Y,Z,TypeID) VALUES (0,0,0,1);

INSERT INTO Primitives(X,Y,Z,TypeID) VALUES (0,1,0,2);

INSERT INTO Primitives(X,Y,Z,TypeID) VALUES (0,0,1,3);

INSERT INTO Primitives(X,Y,Z,TypeID) VALUES (1,0,0,1);

INSERT INTO Primitives(X,Y,Z,TypeID) VALUES (0,0.5,0.5,2);

INSERT INTO Primitives(X,Y,Z,TypeID) VALUES (-0.5,0.5,0,3);

Чтобы выполнить запрос нужно нажать правую кнопку мыши и в меню выбрать Execute Query. Если все сделано правильно, то появится сообщение о том, что 9 строк добавлено.

Я добавил три строки в таблицу PrimitiveTypes (Чайник, Куб и Тор, пока этого хватит) и шесть строк в таблицу Primitives. Обратите внимение на то, что последнее число в скобках в каждом запросе обозначение значение колонки ID в табилице PrimitiveTypes, которое соответствует нужному типу примитива.

Например, можно убедиться, что в таблице PrimitiveTypes чайнику соответствует ID=1 (это может быть и не те так, если запросы выполнены в другом порядке или в некоторых других случаях. Если ID имеет другое значение, то необходимо изменить запросы на добавление примитивов).

Таким образом, для данного запроса (INSERT INTO Primitives(X,Y,Z,TypeID) VALUES (0,0,0,1);) в качестве значения TypeID используется 1, то есть первый примитив будет иметь тип «Чайник».

Данные добавлены, теперь нам нужно добавить файлы, необходимые для работы Entity Framework. Создаем папку Context и ней жмем Add->New Item->Data->ADO.NET Entity Data Model. Я назову файл GameModel.edmx.


В следующем окне нам предложат выбрать подключение к базе данных или создать новое (наше подключение уже должно быть в списке).


Выбираем GameDB.sdf и ставим галочку в строке Save entity connection settings in App.Config as:

В текстовом поле вводим подходящее название: GameDBEntities – хорошее название.

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

<connectionStrings>

<add
name=«GameDBEntities«
connectionString=«metadata=res://*/Context.GameModel.csdl|res://*/Context.GameModel.ssdl|res://*/Context.GameModel.msl;provider=System.Data.SqlServerCe.3.5;provider connection string=&quot;Data Source=D:\my\Articles4\MyGame_DB\MyGame\Data\GameDB.sdf&quot;«
providerName=«System.Data.EntityClient« />

</connectionStrings>

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

<connectionStrings>

<add
name=«GameDBEntities«
connectionString=«metadata=res://*/Context.GameModel.csdl|res://*/Context.GameModel.ssdl|res://*/Context.GameModel.msl;provider=System.Data.SqlServerCe.3.5;provider connection string=&quot;Data Source=Data\GameDB.sdf&quot;«
providerName=«System.Data.EntityClient« /></connectionStrings>

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


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


Эта схема показывает отображение структуры базы данных на классы в нашем приложении. Это означает, что теперь можно будет работать с данными в терминах C# а не SQL (язык запросов).

Если сейчас раскрыть элемент GameModel.edmx в Solution Explorer, и открыть GameModel.Designer.cs, то мы увидим описание всех полученных классов. Например, так будет выглядеть описание класса Primitives, связанного с таблицей Primitives


[global::System.Data.Objects.DataClasses.EdmEntityTypeAttribute(NamespaceName=«GameDBModel», Name=«Primitives»)]

[global::System.Runtime.Serialization.DataContractAttribute(IsReference=true)]

[global::System.Serializable()]


public
partial
class
Primitives : global::System.Data.Objects.DataClasses.EntityObject

{


///
<summary>


/// Create a new Primitives object.


///
</summary>


///
<param name=»id»>Initial value of ID.</param>


public
static
Primitives CreatePrimitives(int id)

{


Primitives primitives = new
Primitives();

primitives.ID = id;


return primitives;

}


///
<summary>


/// There are no comments for Property ID in the schema.


///
</summary>

[global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)]

[global::System.Runtime.Serialization.DataMemberAttribute()]


public
int ID

{


get

{


return
this._ID;

}


set

{


this.OnIDChanging(value);


this.ReportPropertyChanging(«ID»);


this._ID = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value);


this.ReportPropertyChanged(«ID»);


this.OnIDChanged();

}

}


private
int _ID;


partial
void OnIDChanging(int value);


partial
void OnIDChanged();


///
<summary>


/// There are no comments for Property X in the schema.


///
</summary>

[global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute()]

[global::System.Runtime.Serialization.DataMemberAttribute()]


public
global::System.Nullable<double> X

{


get

{


return
this._X;

}


set

{


this.OnXChanging(value);


this.ReportPropertyChanging(«X»);


this._X = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value);


this.ReportPropertyChanged(«X»);


this.OnXChanged();

}

}


private
global::System.Nullable<double> _X;


partial
void OnXChanging(global::System.Nullable<double> value);


partial
void OnXChanged();


///
<summary>


/// There are no comments for Property Y in the schema.


///
</summary>

[global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute()]

[global::System.Runtime.Serialization.DataMemberAttribute()]


public
global::System.Nullable<double> Y

{


get

{


return
this._Y;

}


set

{


this.OnYChanging(value);


this.ReportPropertyChanging(«Y»);


this._Y = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value);


this.ReportPropertyChanged(«Y»);


this.OnYChanged();

}

}


private
global::System.Nullable<double> _Y;


partial
void OnYChanging(global::System.Nullable<double> value);


partial
void OnYChanged();


///
<summary>


/// There are no comments for Property Z in the schema.


///
</summary>

[global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute()]

[global::System.Runtime.Serialization.DataMemberAttribute()]


public
global::System.Nullable<double> Z

{


get

{


return
this._Z;

}


set

{


this.OnZChanging(value);


this.ReportPropertyChanging(«Z»);


this._Z = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value);


this.ReportPropertyChanged(«Z»);


this.OnZChanged();

}

}


private
global::System.Nullable<double> _Z;


partial
void OnZChanging(global::System.Nullable<double> value);


partial
void OnZChanged();


///
<summary>


/// There are no comments for PrimitiveTypes in the schema.


///
</summary>

[global::System.Data.Objects.DataClasses.EdmRelationshipNavigationPropertyAttribute(«GameDBModel», «PR0», «PrimitiveTypes»)]

[global::System.Xml.Serialization.XmlIgnoreAttribute()]

[global::System.Xml.Serialization.SoapIgnoreAttribute()]

[global::System.Runtime.Serialization.DataMemberAttribute()]


public
PrimitiveTypes PrimitiveTypes

{


get

{


return ((global::System.Data.Objects.DataClasses.IEntityWithRelationships)(this)).RelationshipManager.GetRelatedReference<PrimitiveTypes>(«GameDBModel.PR0», «PrimitiveTypes»).Value;

}


set

{

((global::System.Data.Objects.DataClasses.IEntityWithRelationships)(this)).RelationshipManager.GetRelatedReference<PrimitiveTypes>(«GameDBModel.PR0», «PrimitiveTypes»).Value = value;

}

}


///
<summary>


/// There are no comments for PrimitiveTypes in the schema.


///
</summary>

[global::System.ComponentModel.BrowsableAttribute(false)]

[global::System.Runtime.Serialization.DataMemberAttribute()]


public
global::System.Data.Objects.DataClasses.EntityReference<PrimitiveTypes> PrimitiveTypesReference

{


get

{


return ((global::System.Data.Objects.DataClasses.IEntityWithRelationships)(this)).RelationshipManager.GetRelatedReference<PrimitiveTypes>(«GameDBModel.PR0», «PrimitiveTypes»);

}


set

{


if ((value != null))

{

((global::System.Data.Objects.DataClasses.IEntityWithRelationships)(this)).RelationshipManager.InitializeRelatedReference<PrimitiveTypes>(«GameDBModel.PR0», «PrimitiveTypes», value);

}

}

}

}

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

Если появляется необходимось добавить какие-то методы в эти классы нужно создать отдельный файл в папке Context и реализовать в нем нужные методы, например так:

Primitives.cs

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace MyGame.Context

{


public
partial
class
Primitives

{


public
void Foo()

{


// Do something

}

}

}

Теперь у класс Primitives появится новый метод Foo, который ничего не делает.

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

Реклама
Запись опубликована в рубрике Компьютерная графика, Uncategorized с метками , , , , , , . Добавьте в закладки постоянную ссылку.

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

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s