Смекни!
smekni.com

Введение в ObjectSpaces (стр. 2 из 3)

Рисунок 2. ER-диаграмма

Кроме этого, понадобится описать OSD-схему, которая будет описывать объекты C#-кода.

<osd:ExtendedObjectSchema Name="DataTypesOSD" xmlns:osd="http://schemas.microsoft.com/data/2002/09/20/persistenceschema"> <osd:Classes> <osd:Class Name="Rsdn.Samples.Northwind.Customer"> <osd:Member Name="CustomerID" Key="true" /> <osd:Member Name="Company" /> <osd:Member Name="Name" /> <osd:Member Name="Phone" /> <osd:Member Name="Orders" /> </osd:Class> <osd:Class Name="Rsdn.Samples.Northwind.Order"> <osd:Member Name="_orderID" KeyType="AutoIncrement" Hidden="false" Key="true" Alias="OrderID" /> <osd:Member Name="OrderDate" /> <osd:Member Name="RequiredDate" /> <osd:Member Name="ShippedDate" /> <osd:Member Name="EmployeeID" /> <osd:Member Name="Freight" /> <osd:Member Name="Customer" /> </osd:Class> </osd:Classes> <osd:ObjectRelationships> <osd:ObjectRelationship Name="Customers_Orders" Type="OneToMany" ParentClass="Rsdn.Samples.Northwind.Customer" ParentMember="Orders" ChildClass="Rsdn.Samples.Northwind.Order" ChildMember="Customer" /> </osd:ObjectRelationships></osd:ExtendedObjectSchema>

Рисунок 3. Объектная модель.

После объявления RSD- и OSD-схем (рисунок 3), нужно создать Mapping-схему, которая определит отображение одной схемы на другую:

<m:MappingSchema xmlns:m="http://schemas.microsoft.com/data/2002/09/28/mapping"> <m:DataSources> <m:DataSource Name="NorthwindRSD" Type="SQL Server" Direction="Source"> <m:Schema Location="RSD.XML" /> <m:Variable Name="Customers" Select="Customers" /> <m:Variable Name="Orders" Select="Orders" /> <m:Relationship Name="Customers_Orders" FromVariable="Customers" ToVariable="Orders"> <m:FieldJoin From="CustomerID" To="CustomerID" /> </m:Relationship> </m:DataSource> <m:DataSource Name="DataTypesOSD" Type="Object" Direction="Target"> <m:Schema Location="OSD.XML" /> </m:DataSource> </m:DataSources> <m:Mappings> <m:Map SourceVariable="Customers" TargetSelect="Rsdn.Samples.Northwind.Customer"> <m:FieldMap SourceField="CustomerID" TargetField="CustomerID" /> <m:FieldMap SourceField="CompanyName" TargetField="Company" /> <m:FieldMap SourceField="ContactName" TargetField="Name" /> <m:FieldMap SourceField="Phone" TargetField="Phone" /> </m:Map> <m:Map SourceVariable="Orders" TargetSelect="Rsdn.Samples.Northwind.Order"> <m:FieldMap SourceField="OrderID" TargetField="_orderID" /> <m:FieldMap SourceField="OrderDate" TargetField="OrderDate" /> <m:FieldMap SourceField="RequiredDate" TargetField="RequiredDate" /> <m:FieldMap SourceField="ShippedDate" TargetField="ShippedDate" /> <m:FieldMap SourceField="EmployeeID" TargetField="EmployeeID" NullValue="-1" /> <m:FieldMap SourceField="Freight" TargetField="Freight" /> </m:Map> <m:RelationshipMap Source="Customers_Orders" Target="Customers_Orders" /></m:Mappings></m:MappingSchema>

OPath

Одна из основных задач при работе с информацией – это создание запросов для выборки необходимых данных. Так, в случае РСУБД можно использовать язык запросов SQL, для выборки информации из XML-источников у нас есть XPath. Но как SQL, так и XPath – это языки запросов, которые слишком сильно привязаны к модели хранения данных и, как результат, для O/R Mapper приходится применять специальный язык запросов, который позволит создавать запросы к данным в терминах объектной модели приложения и легко транслировать их в язык, понимаемый хранилищем данных (для ObjectSpaces и MS SQL Server это SQL).

Для обращения к источнику данных в ObjectSpaces используется специальный язык запросов – OPath. Синтаксис этого языка (отдаленно он напоминает XPath) позволяет выполнять запросы к источнику данных, основываясь на иерархии классов, используемых в приложении. В настоящее время OPath поддерживает следующий набор операторов (для операторов может использоваться синтаксис как C#, так и VB.NET):

Оператор в C# стиле Оператор в VB стиле Описание
.[] .() Операторы группировки используются для разделения свойств и группировки выражений. Например:Customer[CustomerID=’alfki’].Orders.ShipDate>#11/12/2003#
! not Логическоеотрицание. not (Customer[CustomerID=’alfki’])
*/% */MOD Умножение, деление, получение модуля
+- +- Сложение, вычитание
< > <=>= < > <=>= Сравнение двух значенийCustomer.CreateDate > #12/09/2002#
=!=== =<> == Равенство двух значений
&&|| andor Customer.Region = ‘ru’ || Customer.Region = ‘en’
^ ^ Символ ^ используется для обозначений родитель/потомок. В случае использования оператора ^ следующие два выражения эквивалентны:Orders.OrderDetail[^.OrderDate > #1/1/2003#]Orders.[OrderDate > #1/1/2003#]

ObjectSpace

При работе с сохраняемыми объектами нам нужны следующие возможности – загрузка сохраненных объектов, отслеживание состояния и возврат изменений обратно, в базу данных. Класс ObjectSpace объединяет в себе все эти возможности. Рассмотрим отдельные моменты работы с этим классом.

Создание экземпляра ObjectSpace

Для создания экземпляра ObjectSpace нужно иметь три схемы – RSD, OSD и MSD (при желании их можно скомбинировать в одном XML-файле), а также экземпляр SqlConnection для взаимодействия с источником данных.

// Созданиеэкземпляракласса ObjectSpacesusing (SqlConnection conn = new SqlConnection( "Data Source=tim; Integrated Security=SSPI; Database=northwind")){ ObjectSpace os = new ObjectSpace("map.xml", conn);// Работаем с os. Явно открывать подключение SqlConnection не обязательно. // Это происходит автоматически.}

Запрос к источнику данных

После инициализации экземпляра ObjectSpace можно обратиться к источнику данных. Для этого у класса ObjectSpace есть три метода GetObject, GetObjectReader, GetObjectSet которые позволяют получать данные в виде трех различных форм – одиночный объект, курсор или список.

// Определим “сохраняемые” объекты, которые будем использовать в дальнейшемpublic class Customer { public string CustomerID; public string Name; public string Company; public string Phone; public string Fax; public ArrayList Orders = new ArrayList(); } public class Order { private int _orderID = 0; public int OrderID { get { return _orderID; } } public DateTime OrderDate; public DateTime RequiredDate; public DateTime ShippedDate; public Decimal Freight; public int EmployeeID; public Customer Customer;}// Извлекаем объект Customer (включая подчиненное свойство Orders)// наоснове OPath-запроса (City='Berlin' && Orders.OrderDate < #1998.10.10#).// Для каждого экземпляра класса Customer загружается свойство “Orders”.Customer cust = (Customer)os.GetObject(typeof(Customer), "City='Berlin' && Orders.OrderDate < #1998.10.10#", “Orders”);

Во что выливается вызов приведенного выше метода os.GetObject? Используя Profiler из MS SQL Server, можно увидеть, что в БД будет выполнен следующий SQL-запрос (отформатирован для приведения в более “читаемый” вид):

exec sp_executesql N'select Customers.[CustomerID], Customers.[CompanyName], Customers.[ContactName], Customers.[City], Customers.[Phone] from [Northwind].[dbo].[Customers] as Customers where ((Customers.[City]) = (@p0)) AND (EXISTS( select Orders.[OrderID], Orders.[CustomerID] from [Northwind].[dbo].[Orders] as Orders where ((Customers.[CustomerID]) = (Orders.[CustomerID])) AND ((Orders.[OrderDate]) > (@p1)))) order by 1;select Customers.[CustomerID], Orders.[OrderID], Orders.[CustomerID], Orders.[RequiredDate], Orders.[ShippedDate], Orders.[OrderDate] from [Northwind].[dbo].[Customers] as Customers, [Northwind].[dbo].[Orders] as Orders where (((Customers.[City]) = (@p0)) AND (EXISTS( select Orders.[OrderID], Orders.[CustomerID] from [Northwind].[dbo].[Orders] as Orders where ((Customers.[CustomerID]) = (Orders.[CustomerID])) AND ((Orders.[OrderDate])>(@p1)) ))) AND ((Customers.[CustomerID])=(Orders.[CustomerID])) order by 1, 2, 3 ;', N'@p0 nvarchar(6),@p1 datetime', @p0 = N'Berlin', @p1 = 'Oct 10 1998 12:00:00:000AM'

Создание записей в базе данных

Одно из больших преимуществ в использовании ObjectSpaces состоит в том, что для добавления объекту свойств “сохраняемости” его не надо специальным образом модифицировать (наследовать от специального базового класса, специальным образом размечать свойства или поля). Подобная прозрачность реализации ObjectSpaces дает преимущества в использовании.

// Работа с объектами Customer и Orders не зависит // от того, используется ObjectSpaces или нетCustomer cust = new Customer();Order ord = new Order();cust.Id = "ALFQI";cust.Name = "MyName";cust.Company = "MyCompany";cust.Phone = "MyPhone";cust.Fax = "MyFax";ord.Customer = cust;ord.OrderDate = DateTime.Now;ord.ShippedDate = DateTime.Now;ord.RequiredDate = DateTime.Now;cust.Orders.Add(ord);// Перед сохранением объектов необходимо поместить их в контекст // ObjectSpaces. Флаг InitialState.Inserted показывает, что мы добавляем новую // записьвбазуданныхos.StartTracking(ord, InitialState.Inserted);os.StartTracking(cust, InitialState.Inserted);// Сохраняемэкземпляркласса Customer. // Параметр PersistenceOptions(Depth.ObjectGraph) сообщает, // что будет сохранен весь граф объектов.os.PersistChanges(cust, new PersistenceOptions(Depth.ObjectGraph));

Удаление записей с использованием ObjectSpaces

Существующая версия ObjectSpaces поддерживает удаление объектов только в том случае, если они ранее были добавлены в контекст ObjectSpaces.

ПРИМЕЧАНИЕДля удаления объекта из базы данных его необходимо предварительно добавить в контекст ObjectSpaces. Это можно сделать, используя методы GetObject, GetObjectReader, GetObjectSet класса ObjectSpace, или добавить объект в контекст самостоятельно с помощью метода StartTracking
Customer cust = new Customer();cust.Id = "ALFQI";// Перед операцией над объектом необходимо поместить его в контекст // ObjectSpaces. Флаг InitialState.Unchanged показывает, что объект ранее// былсохраненвбазеданныхos.StartTracking(cust, InitialState.Unchanged);// Помечаем экземпляр класса Customer как удаляемый. os.MarkForDeletion(cust);// Сохраняем изменения в базе данныхos.PersistChanges(cust);

Отложенная загрузка данных