Текст книги "Программирование мобильных устройств на платформе .NET Compact Framework"
Автор книги: Иво Салмре
Жанр:
Программирование
сообщить о нарушении
Текущая страница: 8 (всего у книги 69 страниц)
Данные в современных базах данных хранятся в виде наборов взаимосвязанных таблиц. Для работы с реляционными данными такого рода в NET Framework предлагается объектная модель под названием ADO.NET. Модель ADO.NET предоставляет в распоряжение разработчиков классы, позволяющие управлять в памяти набором связанных между собой реляционных таблиц, а также классы, обеспечивающие различные представления этих данных. О данных, для управления которыми используется модель ADO.NET, говорят, что они находятся в объекте DataSet.
В чем состоит разница между ADO и ADO.NET?
На протяжении последних 10-15 лет компания Microsoft предложила ряд различных библиотек объектов для доступа к данным, каждая из которых улучшала предшествовавшую модель. До появления ориентированной на использование управляемого кода технологии ADO NET применялась технология ADO название которой является аббревиатурой от ActiveX Data Objects (объекты данных ActiveX), и которая предназначалась главным образом для того, чтобы предоставить возможность обращаться к информации, хранящейся в базах данных, разработчикам на Visual Basic. В настоящее время все предыдущие модели (вместе с их многочисленными загадочными трехбуквенными акронимами наподобие DAO, RDO, ADO и номерами различных версий – прошу прощения, если я что-то перепутал!) вытеснены ADO NET.
В то время как предыдущими технологиями доступа к данным, такими как ADO, предлагались объекты RowSet, каждый из которых представлял одиночную таблицу данных с курсором, указывающим на текущую строку, в ADO.NET предлагаются объекты DataSet, представляющие наборы связанных таблиц, хранящихся в памяти вместе с межтабличными ссылками на объекты.
В ADO.NET данные представляются не в виде изолированной таблицы с курсором, обеспечивающим перемещение между строками, а в виде хранящейся в памяти схемы таблиц, навигация между которыми осуществляется с использованием объектно-ориентированных механизмов. В ADO.NET не существует понятия курсора текущей строки, поскольку данные явным образом отделены от любых подключений к базе данных, для которых мог бы потребоваться курсор. Для связывания объектов ADO.NET DataSet с внутренними источниками данных в ADO.NET используются объекты DataReader. Объекты DataReader предоставляют лишь возможность однонаправленного считывания данных из источника данных. Эти объекты используются внутренним образом для заполнения данными объектов ADO.NET DataSet.
При работе с наборами данных DataSet, хранящимися в памяти, разработчики имеют возможность выполнять над ними целый ряд операций, включая следующие:
■ Сохранение внесенных текущих изменении в базах данных. Интерфейс между объектами ADO.NET DataSet и базами данных обеспечивается классом DataAdapter. Классы DataAdapter получают список добавлений, обновлений и удалений, относящийся к DataSet, и применяют соответствующую логику для распространения этих изменений на основную базу данных. Обычно это делается путем выполнения SQL-операторов или вызова хранимых процедур, предназначенных для работы с базой данных.
■ Сохранение данных в виде XML-документа. Объекты DataSet можно сериализовать в документы XML и сохранять в виде файлов с возможностью их последующей повторной загрузки.
■ Пересылка данных другому звену. Поскольку объекты DataSet, включая информацию о внесенных в них изменениях, без труда переводятся в форму постоянно существующих XML-документов, можно легко осуществлять их передачу между различными звеньями вычислительной системы посредством вызовов Web-служб.
■ Локальное использование данных. Объекты DataSet можно использовать как мощную абстракцию для работы с базами данных в памяти. Если эти данные предназначены только для чтения то внесенные в них локальные изменения не распространяются на данные, расположенные на сервере или в долговременном хранилище.
Вынесение полезной отладочной и проектной информации в необязательные компоненты
Управляемый код может содержать большое количество информации, которая может быть полезной для разработчиков при отладке каркасов, компонентов и приложений. В качестве примера подобных данных, привносящих добавленную стоимость, можно привести текстовые описания возможных исключений, которые возникают в процессе выполнения кода. Одно дело, когда сообщения об ошибках выводятся в виде малоинформативных фраз наподобие "Неизвестное исключение" или "Возбуждено исключение System.IO.SomeRandomException", и совершенно другое, когда вы получаете понятное простому смертному сообщение, подробно описывающее суть происшедшего и его наиболее вероятную причину. Информация подобного рода полезна на стадиях тестирования и отладки, но при разработке таких крупных проектов, как NET Compact Framework, для хранения всех строк сообщений об ошибках может потребоваться слишком много места, что в случае мобильных устройств является непозволительной роскошью.
.NET Framework для настольных компьютеров просто включает в себя обширную информацию об исключениях наряду с другими ресурсами. Эта информация доступна не только на английском, но и на многих других основных языках, для которых была выполнена локализация Visual Studio; это означает, что конечные пользователи имеют возможность получать богатую диагностическую информацию на своем родном языке. Чтобы обеспечить доступ к этой информации программным путем, каждое исключение управляемого кода имеет свойство .Message, открывающее программный доступ к соответствующему тексту.
Наличие текстовых строк с богатой отладочной информацией является несомненным преимуществом, однако за это преимущество приходится платить увеличением размера системы. Изначально ясно, что для подробных текстовых описаний требуется выделять много места. Для этого потребуется еще больше места, если предусмотрена локализация описаний. В связи с этим возникает вопрос о возможных проектных ограничениях, поскольку задачи обеспечения компактности системы и предоставления как можно более подробной текстовой информации вступают в конфликт между собой.
В .NET Compact Framework эта проблема решается за счет разбиения указанной информации на отдельные вспомогательные компоненты не входящие в базовый состав NET Compact Framework. Эти вспомогательные компоненты могут устанавливаться поверх .NET Compact Framework в соответствии с необходимостью или желанием. Файл, содержащий эту информацию, имеет название System.SR.dll, а его размер составляет 91 Кбайт. Для других языков локализации предусмотрены дополнительные версии этого файла. Всего в настоящее время существует 10 таких локализованных языковых версий, суммарный размер которых достигает 900 Кбайт, что составляет ощутимую долю общего размера .NET Compact Framework. Таким образом, исключение этих строк из .NET Compact Framework позволяет значительно уменьшить суммарный размер системы.
При установке .NET Compact Framework в ОЗУ или ПЗУ мобильных устройств указанные вспомогательные файлы, как правило, не устанавливаются; обычно их устанавливают лишь тогда, когда это необходимо или желательно. В среде разработки Visual Studio .NET эти файлы автоматически устанавливаются во время отладки, поскольку их присутствие почти всегда является желательным для разработчиков
Если во время выполнения возбуждается исключение, .NET Compact Framework проверяет, присутствует ли на устройстве соответствующая языковая версия файла с диагностическими текстами исключений. Если это так, то осуществляется доступ к нужному файлу и загружается соответствующее текстовое описание. В противном случае NET Compact Framework пытается найти языково-инвариантную (английскую) версию ресурсного файла со строками описаний исключений. Если этот файл присутствует, выполняется загрузка текста. Если же файл отсутствует, отображается заданный по умолчанию текст, в котором ошибка не конкретизируется (хотя программное имя исключения и может дать некоторую информацию о ее природе).
Использование механизма необязательных вспомогательных компонентов в .NET Compact Framework позволило достичь разумного баланса между максимально возможным уменьшением размера системы и предоставлением разработчикам как можно более полной отладочной информации
Аналогичная стратегия проектирования применяется и в тех случаях, когда компоненты могут предоставить на стадии проектирования некую дополнительную информацию, которая не потребуется во время выполнения. В процессе создания пользовательских элементов управления можно создать отдельные версии компонентов для стадии проектирования и времени выполнения, различающиеся тем, что компоненты, используемые на стадии проектирования, содержат дополнительные метаданные, интерпретация которых на стадии разработки предоставляет расширенную информацию, позволяющую принимать более эффективные проектные решения
Аналогичные модели, предусматривающие создание необязательных файлов ресурсов или отдельных версий компонентов для стадии проектирования, вы можете применять и в своей практике проектирования приложений, если это может привести к существенному уменьшению размера приложения.
Средства подключения к базам данных SQL СЕ/SQL
Необязательные компоненты другой разновидности, которые могут устанавливаться поверх .NET Compact Framework, обеспечивают связь с определенными базами данных. Когда поставлялась версия 1.1 .NET Compact Framework, одновременно с ней поставлялись ее необязательные компоненты, обеспечивающие доступ к локальным базам данных SQL СЕ и удаленным базам данных SQL Server. Не являясь частью базового состава .NET Compact Framework, эти компоненты оказываются весьма полезными при создании приложений, работающих с упомянутыми базами данных. Существуют также аналогичные компоненты, обеспечивающие доступ к базам данных сторонних разработчиков.
Элементы, отсутствующие в первой версии .NET Compact Framework
Защита доступаКонцепция явной проверки полномочий являлась центральной при проектировании общеязыковой среды выполнения (Common Language Runtime), лежащей в основе как .NET Framework, так и NET Compact Framework. Другим распространенным на званием этой концепции является безопасность доступа кода.
Средства безопасности доступа кода обеспечивают возможность управления предварительно определяемыми политиками, разрешающими выполнение кода на основании подтверждений, предоставляемых самим кодом. Такими подтверждениями могут, например, служить криптографическая подпись, подтверждающая подлинность издателя компонента, полное имя самого компонента, местоположение установки компонента в локальной файловой системе или URL-адрес, с которого был загружен компонент. На основании предоставленного подтверждения и локальной политики, определенной для той машины, на которой должен выполняться код, коду предоставляются те или иные полномочия. В качестве примера можно привести следующие возможные уровни полномочий:
■ Полные доверительные отношения. Код выполняется в системе на уровне полных доверительных отношений и может делать все то, что могло бы делать собственное приложение.
■ Ограниченный доступ к файловой системе. Операции файлового ввода-вывода могут быть запрещены или ограничены некоторыми каталогами, исходя из подтверждений, предоставленных компонентом.
■ Ограниченный доступ к пользовательскому интерфейсу. Возможность вызова пользовательских интерфейсов может быть разрешена или запрещена компонентам.
■ Сетевой доступ. Возможность доступа к сети может быть разрешена или запрещена компонентам.
■ Доступ к собственному коду. Возможность осуществления вызовов собственных кодов может быть разрешена или запрещена компонентам.
В варианте .NET Framework, ориентированном на настольные компьютеры и серверы, доступны не только эти, но и многие другие разрешения, которые могут определяться в качестве политик. По данному вопросу имеется обширная литература и документация.
.NET Compact Framework проектировалась с учетом поддержки описанной модели безопасности, основанной на политиках. Однако в первом выпуске .NET Compact Framework политика безопасности определялась очень просто: "Весь код пользуется полными доверительными отношениями". Это означает, что приложения, предназначенные для .NET Compact Framework v1.1, выполняющейся на устройстве, будут выполняться в этой среде с тем же набором разрешений, с которым на устройстве выполняются собственные коды.
Решение о закреплении разрешенных полномочий на уровне "выполнения в условиях полных доверительных отношений" для первого выпуска .NET Compact Framework было продиктовано исключительно прагматическими соображениями. В ходе обсуждений, сопровождавших ранние опыты внедрения данного продукта, стало очевидным, что первая волна конкурентоспособных приложений для мобильных устройств будет, скорее всего, представлена приложениями, которые явным образом устанавливаются на устройствах пользователями или администраторами, а не загружаются и выполняются на лету. Подобные виды приложений начали уже в заметной степени вытеснять традиционные приложения для устройств на основе собственных кодов, и поэтому использование аналогичной модели полномочий вполне соответствовало сложившейся ситуации. Если бы мы располагали неограниченным временем для проектирования, построения, тестирования и окончательного уточнения политик безопасности на основании откликов пользователей, то модель, используемая в первой версии, была бы проработана более тщательно. Однако в результате многочисленных дискуссий мы поняли, что не это требование будет определять успешность выполнения приложений в нашей первой версии продукта. В силу этого создание более совершенного механизма политик безопасности было отложено до следующего выпуска, что дало возможность сконцентрировать усилия на разработке других средств, вошедших в первую версию. Это решение далось нелегко, однако, по прошествии уже достаточно большого времени, мы по-прежнему убеждены в его правильности. Следует отметить, что на некоторых из устройств, пользующихся массовым спросом, например, на некоторых смартфонах, эксплуатируются политики безопасности, требующие, чтобы приложения, основанные на собственных кодах, были снабжены криптографическими подписями; в случае таких устройств в отношении приложений, основанных на управляемом коде, действуют те же политики.
В дальнейшем желательно внедрить модель безопасности, поддерживающую загрузку кода на лету и присвоение полномочий на основе подтверждений, предоставляемых кодом. Поэтому весьма вероятно, что в будущих версиях .NET Compact Framework будет предусмотрено несколько различных уровней доверительности и полномочий, охватывающих интервал от "полного доверия" на основе предоставляемых подтверждений до весьма ограниченных возможностей для кода, пользующегося низким уровнем доверительности. Кроме того, различные типы мобильных устройств будут, вероятно, характеризоваться заданием на них различных политик. На некоторых устройствах политики будут определяться конечными пользователями, на других – администраторами, изготовителями оборудования или же сетевыми операторами, обслуживающими то или иное устройство. Поддержка этих возможностей будет осуществляться встроенной моделью обеспечения безопасности доступа.
МультимедиаЕще одной областью, от явной поддержки которой в первом выпуске .NET Compact Framework мы решили отказаться, является работа с мультимедийной информацией. Это, например, означает, что разработчики, желающие воспроизводить звуки или показывать видео, должны использовать для этой цели собственные коды. Как и в предыдущем случае, если бы для разработки, тестирования и доработки этих средств у нас было больше времени, то их поддержка была бы обеспечена уже в первом выпуске .NET Compact Framework. Анализ отзывов, полученных нами от лиц, проводивших первые испытания этого продукта, позволил сделать вывод, что применительно к тем видам приложений, в создании которых в настоящее время существует потребность, поддержка данных возможностей не являлась критическим фактором успешности. По всей вероятности, в будущих версиях .NET Compact Framework поддержка мультимедийной информации будут более интенсивной.
Оглядываясь назад, мы можем констатировать, что принятое нами решение было правильным.
Как запускается и выполняется код
При первоначальном запуске приложения на основе управляемого кода загрузка, верификация и запуск кода осуществляются за несколько шагов. Когда такое приложение уже запущено и выполняется, среда выполнения управляемого кода продолжает играть важную роль во время загрузки новых классов и JIT-компиляции кода, когда это оказывается необходимым, а также в процессе управления памятью, отводимой для приложения. Полезно иметь более или менее полное общее представление о том, как все это происходит. Процесс запуска и выполнения приложения можно пояснить при помощи описанных ниже шести шагов:
1. Загружается управляемое приложение. Двоичный заголовок приложения указывает операционной системе на то, что оно не является приложением в собственных кодах. Вместо того чтобы просто предоставить возможность выполняться инструкциям кода, загружается исполнительный механизм .NET Compact Framework, которому сообщается о том, что он должен запустить приложение на выполнение.
2. Исполнительный механизм находит класс, содержащий "главную" точку входа "main", с которой должно начинаться выполнение приложения. Таковым является класс с сигнатурой функции, соответствующей виду static void Main(). В случае обнаружения типа с такой сигнатурой происходит загрузка класса и производится попытка выполнения "главной" процедуры (шаги 3 и 4).
3. Загружается класс. Информация о классе загружается и верифицируется с той целью, чтобы убедиться в корректности и согласованности определения класса. Все методы (то есть точки входа исполняемого кода) класса обозначаются как "некомпилированные".
4. Исполнительный механизм пытается выполнить указанную процедуру. Если уже имеется откомпилированный код, связанный с процедурой, осуществляется выполнение этого кода.
5. Если исполнительный механизм обнаруживает, что свойство или метод, которые требуется запустить на выполнение, не откомпилированы, они компилируются по требованию. Производится загрузка содержащейся в классе информации, которая необходима методу. Код верифицируется для гарантии того, что он содержит безопасные, допустимые и корректно сформированные IL-инструкции, а затем подвергается JIT-компиляции. Если метод ссылается на другие типы, которые еще не были загружены, осуществляется необходимая загрузка определений соответствующих классов и типов. Методы, содержащиеся в классах, не компилируются до тех пор, пока в этом не возникнет необходимости; именно в этом и состоит смысл JIT-компиляции.
6. Выполняется скомпилированный к этому моменту метод. Если возникает необходимость в распределении памяти для типов или методов, исполнительному механизму направляются соответствующие запросы. Любые вызовы классов методами возвращают нас к шагу 5.
На первый взгляд, вышеперечисленные действия требуют выполнения большого объема работы, однако в действительности все происходит очень быстро.
Что такое "тип"?
На заметку! "Типом" являются любые данные, с которыми может работать программист. Это можно сформулировать и иначе, сказав, что типами являются любые типы данных и классы. Все является типом, включая целые и десятичные числа, другие скалярные величины, строки и классы. Любая "сущность", которой вы можете манипулировать внутри кода, является экземпляром типа. Классы являются специальными типами в том смысле, что они содержат не только данные, но и код, и поддерживают такие объектно-ориентированные свойства, как наследование.
На заметку! После загрузки определений классов и других типов в память, их верификации и компиляции эти данные размещаются в распределенной памяти точно так же, как и любые другие данные, которые разработчики распределяют в своих приложениях. Рассмотрим пример. Предположим, вы создаете объект ArrayList:
System.Collection.ArrayList aList = new System.Collection.ArrayList();При этом выполняются следующие операции:
1. Вышеприведенный код представляется в вашем приложении на промежуточном языке IL. Этот IL-код подвергается JIT-компиляции во время выполнения непосредственно перед тем, как выполняться в первый раз. Этот код помещается в распределенную память.
2. По окончании JIT-компиляции кода исполнительный механизм пытается определить, загружена ли информация о типе System.Collections.Array в память; если это не так, выполняется распределение памяти для этого типа, а также загрузка и связывание определения с классом ArrayList.
3. Во время выполнения приведенного выше фактического кода должен быть выполнен конструктор объекта ArrayList. Если соответствующий код еще не загружался и не подвергался JIT-компиляции, осуществляется распределение памяти для кода конструктора и JIT-компиляция этого кода. Выполняется загрузка кода, его верификация и JIT-компиляция, и память, распределенная для кода этого конструктора, связывается с конструктором класса ArrayList.
То же самое относится и к любому другому загружаемому типу или вызываемому методу; если необходимая работа по их загрузке не была до этого выполнена, они загружаются и компилируются по мере необходимости, и используемая для этого память связывается с типом.
Описанное распределение и отслеживание памяти играет очень важную роль. Как далее будет показано, к данным о коде и определениях классов, как и к любым другим объектам, могут применяться операции сборки мусора.







