Курс лекций для студентов специальности i-31 03 04 Информатика всех форм обучения Минск 2010



страница18/29
Дата09.08.2019
Размер0.64 Mb.
#126834
ТипКурс лекций
1   ...   14   15   16   17   18   19   20   21   ...   29

Сериализация контрактов данных


Контракт данных – это тип (класс или структура), описывающий информационный фрагмент. Если в качестве контракта данных используется обычный класс, информационный фрагмент образуют открытые поля и свойства. Можно пометить тип атрибутом [DataContract]. Тогда информационный фрагмент будут составлять поля и свойства, помеченные атрибутом [DataMember]1. Видимость элементов при этом роли не играет.

[DataContract]

public class Student

{

[DataMember]



public string Name { get; set; }
[DataMember]

public int Age { get; set; }


[DataMember]

public double GPA { get; set; }

}

Атрибут [DataContract] имеет свойства Name и Namespace для указания имени и пространства имён корневого XML-элемента. У атрибута [DataMember] есть свойство Name, а также свойства Order (порядок сериализации членов контракта), IsRequired (обязательный элемент в сериализованном потоке), EmitDefaultValue (запись в поток значений элемента по умолчанию).



Если контракт будет десериализоваться в объекты потомков своего типа, эти типы должны быть упомянуты при помощи атрибута [KnownType]2.

[DataContract]

[KnownType(typeof(Postgraduate))]

public class Student { . . . }


public class Postgraduate : Student { . . . }

Если контракт является коллекцией объектов (как класс Group), он маркируется атрибутом [CollectionDataContract]. Кроме этого, для методов контракта данных применимы атрибуты [OnSerializing], [OnSerialized], [OnDeserializing], [OnDeserialized].

Для сериализации контракта данных используются классы:


  • DataContractSerializer  сериализует контракт;

  • NetDataContractSerializer  сериализует данные и тип контракта;

  • DataContractJsonSerializer  сериализует контракта в формате JSON.

Рассмотрим ряд примеров сериализации контрактов данных:

var student = new Student { Name = "Smirnov", Age = 18, GPA = 9 };


// конструктор DataContractSerializer требует типа контракта данных

var ds = new DataContractSerializer(typeof(Student));


// сериализация (по умолчанию используется формат XML)

using (Stream s = File.Create("studentDS.xml"))

{

ds.WriteObject(s, student);



}
// десериализация

using (Stream s = File.OpenRead("studentDS.xml"))

{

student = (Student)ds.ReadObject(s);



}
// сериализация и десериализация в двоичном формате

using (Stream bs = File.Create("studentDS.bin"))

{

using (XmlDictionaryWriter w = XmlDictionaryWriter.CreateBinaryWriter(bs))



{

ds.WriteObject(w, student);

}

}
using (Stream bs = File.OpenRead("studentDS.bin"))



{

using (XmlDictionaryReader r = XmlDictionaryReader.CreateBinaryReader(bs,

XmlDictionaryReaderQuotas.Max))

{

student = (Student)ds.ReadObject(r);



}

}
// сериализация в формате JSON

var jsonds = new DataContractJsonSerializer(typeof(Student));

using (Stream s = File.Create("student.json"))

{

jsonds.WriteObject(s, student);



}
// используя NetDataContractSerializer, тип указывать не нужно

var netds = new NetDataContractSerializer();

using (Stream s = File.Create("studentNDS.xml"))

{

netds.WriteObject(s, student);



}

XML-сериализация


Сериализацию в формате XML можно выполнить при помощи класса XmlSerializer из пространства имён System.Xml.Serialization. При таком подходе сохраняются public-элементы объекта. Кроме этого, тип объекта должен быть открытым и иметь public-конструктор без параметров.

public class Student

{

public string Name { get; set; }



public int Age { get; set; }

public double GPA { get; set; }

}
var student = new Student {Name = "Smirnov", Age = 18, GPA = 9};
// при создании XmlSerializer требуется указать сериализуемый тип

var serializer = new XmlSerializer(typeof(Student));


// сериализация

using (Stream stream = File.Create("student.xml"))

{

serializer.Serialize(stream, student);



}
// десериализация

using (Stream stream = File.OpenRead("student.xml"))

{

student = (Student) serializer.Deserialize(stream);



}

Настройка XML-сериализации может быть выполнена при помощи атрибутов. [XmlRoot] применяется к типу и задаёт корневой элемент в XML-файле. При помощи [XmlElement] настраивается имя и пространство имён XML-элемента. [XmlAttribute] используется, если член класса требуется сохранить в виде XML-атрибута. Поля и свойства, которые не должны сохраняться, помечаются атрибутом [XmlIgnore]. Если тип содержит коллекцию объектов, то настройка имени этой коллекции и имени отдельного элемента выполняется при помощи атрибутов [XmlArray] и [XmlArrayItem].

public class Student

{

[XmlAttribute("name")]



public string Name { get; set; }

[XmlAttribute("age")]

public int Age { get; set; }

[XmlIgnore]

public double GPA { get; set; }

}
[XmlRoot("students")]

public class Group

{

public Student BestStudent { get; set; }



[XmlArray("list")]

[XmlArrayItem("student")]

public List List { get; set; }

}

Отметим, что XML-сериализация не может сохранить коллекции-словари. Если выполняется XML-сериализация объекта, реализующего интерфейс IEnumerable (обычный или универсальный), сохраняются только те элементы, которые доступны через перечислитель.


2.21. Состав и взаимодействие сборок


В платформе .NET сборка (assembly) – это единица развёртывания и контроля версий. Сборка состоит из одного или нескольких программных модулей и, возможно, данных ресурсов. Эти компоненты могут размещаться в отдельных файлах, либо содержаться в одном файле. В любом случае, сборка содержит в некотором из своих файлов манифест, описывающий состав сборки. Будем называть сборку однофайловой, если она состоит из одного файла. В противном случае сборку будем называть многофайловой. Тот файл, который содержит манифест сборки, будем называть главным файлом сборки.

pic1.emf

Рис. 3. Однофайловая и многофайловая сборки.

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


  1. Ресурсы (текстовые строки, изображения и т. д.) можно хранить вне приложения, что позволяет при необходимости изменять ресурсы без перекомпиляции приложения.

  2. Если исполняемый код приложения разделён на несколько модулей, то модули загружаются в память только по мере надобности.

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

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

public static class TextClass

{

public static string GetText()



{

return "message";

}

}

Файл TextClass.cs с исходным кодом класса TextClass скомпилируем в виде модуля (обратите внимание на ключ компилятора):



csc.exe /t:module TextClass.cs

После компиляции получим файл-модуль TextClass.netmodule. Далее, создадим консольное приложение (файл MainClass.cs):

using System;
public class MainClass

{

public static void Main()



{

Console.WriteLine("Text from resource");

Console.WriteLine(TextClass.GetText());

}

}



Теперь соберём многофайловую сборку. Ключ компилятора /addmodule позволяет добавить к сборке ссылку на внешний модуль. Он должен применяться для каждого подключаемого модуля.

csc.exe /addmodule:textclass.netmodule MainClass.cs

В итоге получим многофайловую сборку, состоящую из двух файлов: главного файла mainclass.exe и файла-модуля textclass.netmodule. Мы можем создать новую сборку, в которой используется код из модуля textclass.netmodule, то есть сделать этот модуль разделяемым между несколькими сборками. Важное замечание: предполагается, что все файлы, составляющие нашу многофайловую сборку, размещены в одном каталоге.

Рассмотрим вопрос взаимодействия сборок. Как правило, крупные программные проекты состоят из нескольких сборок, связанных ссылками. Среди этих сборок имеется некая основная (обычно оформленная как исполняемый файл *.exe), а другие сборки играют роль подключаемых библиотек с кодом необходимых типов (обычно такие сборки – это файлы с расширением *.dll).

Представим пример, который будет использоваться в дальнейшем. Пусть имеется класс (в файле UL.cs), содержащий «полезную» функцию:

namespace UsefulLibrary

{

public class UsefulClass



{

public void Print()

{

System.Console.WriteLine("Useful function");



}

}

}



Скомпилируем данный класс как библиотеку типов (расширение *.dll):

csc.exe /t:library UL.cs

Пусть основное приложение (файл main.cs) собирается использовать код из сборки UL.dll:

using System;

using UsefulLibrary;
public class MainClass

{

public static void Main()



{

// используем класс из другой сборки

UsefulClass a = new UsefulClass();

a.Print();

}

}

Ключ компилятора /r (или /reference) позволяет установить ссылку на требуемую сборку, указав путь к ней. Скомпилируем приложение main.cs:



csc.exe /r:UL.dll main.cs

Платформа .NET разделяет сборки на локальные (или сборки со слабыми именами) и глобальные (или сборки с сильными именами). Если UL.dll рассматривается как локальная сборка, то при выполнении приложения она должна находиться в том же каталоге, что и main.exe1. Локальные сборки обеспечивают простоту развёртывания приложения (все его компоненты сосредоточены в одном месте) и изолированность компонентов. Имя локальной сборки – слабое имя – это имя файла сборки без расширения.

Хотя использование локальных сборок имеет свои преимущества, иногда необходимо сделать сборку общедоступной. До появления платформы .NET доминировал подход, при котором код общих библиотек помещался в системный каталог простым копированием фалов при установке. Такой подход привел к проблеме, известной как «ад DLL» (DLL Hell). Инсталлируемое приложение могло заменить общую библиотеку новой версией, при этом другие приложения, ориентированные на старую версию библиотеки, переставали работать. Для устранения «ада DLL» в платформе .NET используется специальное защищенное хранилище сборок (Global Assembly Cache, GAC).

Сборки, помещаемые в GAC, должны удовлетворять определённым условиям. Во-первых, такие глобальные сборки должны иметь цифровую подпись. Это исключает подмену сборок злоумышленниками. Во-вторых, для глобальных сборок отслеживаются версии и языковые культуры. Допустимой является ситуация, когда в GAC находятся разные версии одной и той же сборки, используемые разными приложениями.

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

Name, Version=1.2.0.0, Culture=neutral, PublicKeyToken=1234567812345678

Рассмотрим процесс создания сборки с сильным именем на примере сборки UL.dll. Первое: необходимо создать пару криптографических ключей для цифровой подписи сборки. Для этих целей служит утилита sn.exe, входящая в состав Microsoft .NET Framework SDK.

sn.exe -k keys.snk

Параметр -k указывает на создание ключей, keys.snk – это файл с ключами. Просмотреть полученные ключи можно, используя команду sn.exe -tp.

Далее необходимо подписать сборку полученными ключами. Для этого используется специальный атрибут1 уровня сборки [AssemblyKeyFile] или ключ компилятора командной строки /keyfile:

using System;

using System.Reflection;

[assembly: AssemblyKeyFile("keys.snk")]

namespace UsefulLibrary { . . . }

Обратите внимание: для использования атрибута необходимо подключить пространство имен System.Reflection, а в качестве параметра атрибута указывается полное имя файла с ключами.

После подписания сборку можно поместить в GAC. Простейший вариант сделать это – использовать утилиту gacutil.exe, входящую в состав .NET Framework SDK. При использовании ключа /i сборка помещается в GAC, а ключ /u удаляет сборку из GAC:

gacutil.exe /i ul.dll

Теперь сборка UL.dll помещена в GAC. Ее сильное имя (для ссылки в программах) имеет вид:

UL, Version=0.0.0.0, Culture=neutral, PublicKeyToken= ff824814c57facfe

Компонентом сильного имени является версия сборки. Если программист желает указать версию, то для этого используется атрибут [AssemblyVersion]. Номер версии имеет формат Major.Minor.Build.Revision. Часть Major является обязательной. Любая другая часть может быть опущена (в этом случае она полагается равной нулю). Часть Revision можно задать как *, тогда компилятор генерирует её как количество секунд, прошедших с полуночи, деленное на два. Часть Build также можно задать как *. Тогда для неё будет использовано количество дней, прошедших с 1 февраля 2000 года.



Каталог: images
images -> В списке студентов (или магистрантов)
images -> Н. И. Сулейманов Комплект контрольно-оценочных средств для оценки результатов освоения профессионального модуля разработан на основе Федерального государственного образовательного стандарта среднего профессионального
images -> По направлению подготовки
images -> Добавить гаджеты. Добавление гаджетов
images -> Техническое задание № apnip/C. 2/CS/Ind/01 Международный консультант по улучшенной производительности орошаемого земледелия
images -> Комплект контрольно-оценочных средств по профессиональному модулю пм. 01 Техническое обслуживание и ремонт автотранспорта


Поделитесь с Вашими друзьями:
1   ...   14   15   16   17   18   19   20   21   ...   29




База данных защищена авторским правом ©vossta.ru 2022
обратиться к администрации

    Главная страница