Опубликован: 05.08.2010 | Доступ: свободный | Студентов: 2009 / 47 | Оценка: 4.50 / 4.40 | Длительность: 60:26:00
Лекция 6:

Работа с потоками данных

< Лекция 5 || Лекция 6: 12345678910111213

Пример 3. Использование файловой библиотеки рисунков

Чтобы воспользоваться созданной библиотекой, а заодно и проверить правильность работы того, что мы так горячо и бодро обсуждали, построим код, позволяющий пользователю извлекать упакованные рисунки и просматривать их. Здесь также без потоков не обойтись. Консольное окно нам здесь не поможет, окно со списком выбора примеров - тоже, придется создать новое окно для управления рисунками. Код вызова нового окна поместим в функцию Example3, а само окно в отдельном файле. Окно просмотра рисунков построим по технологии WPF - там ' красивше ' смотрится растровая графика.

  • В панели Solution Explorer выделите узел проекта App7 и командой меню оболочки Project/Add Window добавьте новое окно WPF с именем Window3.xaml

Немного обленился описывать, поэтому выдам готовое, по коду (по ходу) легко разберетесь...

  • Заполните в файле Window1.xaml.cs функцию Example3() следующим кодом (приводится полностью)
void Example3()
        {
            // Предотвращение повторного создания дочернего окна
            bool existsWindow = false;
            foreach (Window window in Application.Current.Windows)// Перебираем коллекцию
            {
                if (window.Name == "_Window3")
                {
                    // Нашли среди существующих 
                    existsWindow = true;
                    window.Activate();// Сдвинуть на передний план
                    break;// Прервать перебор
                }
            }
    
            if (!existsWindow)// Если не существует, то создать и показать
            {
                Window3 wnd = new Window3();
                // Перед показом проверяем, выбрана ли библиотека
                if (wnd.NameLibrary != String.Empty)
                    wnd.Show();    // Показываем окно Window3
                else
                    wnd.Close();   // Закрываем окно Window3
            }
        }
  • В файле Window1.xaml измените заголовок списка для ' Пример 3 ' на ' Пример 3. Использование файловой библиотеки '
  • Заполните файл Window3.xaml следующей интерфейсной разметкой (приводится полностью)
<Window x:Class="App7.Window3"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Использование файловой библиотеки рисунков" 
    Height="300"
    Width="470"
    MinHeight="300"
    MinWidth="450" 
    Name="_Window3" 
    Closing="Window_Closing"
    >
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="100" />
        </Grid.ColumnDefinitions>
        <Border Background="Black">
            <Image Name="image" Stretch="Uniform" />
        </Border>
        <ListBox Name="listBox" Grid.Column="1" Padding="5,0"
                 ScrollViewer.VerticalScrollBarVisibility="Visible"
                 SelectionChanged="listBox_SelectionChanged">
        </ListBox>
    </Grid>
</Window>
  • Заполните файл Window3.xaml.cs следующим процедурным кодом (приводится полностью)
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
    
// Дополнительные пространства имен
using FORMS = System.Windows.Forms; // Псевдоним
using System.IO;
using System.Configuration;         // Для SettingsProperty
using IO = System.IO;               // Псевдоним для адресации Path
    
namespace App7
{
    public partial class Window3 : Window
    {
        FileStream fs = null;   // Поле файлового потока для видимости в классе
        BinaryReader reader;    // Поле читателя для видимости в классе
        String nameLibrary;     // Опорное поле для свойства с именем библиотеки
        int count;              // Поле под число рисунков
        string[] names;         // Поле под имена рисунков
        long offsetHead;        // Поле начала блока локализации рисунков
        Properties.Settings settings; // Параметры
    
        // Конструктор
        public Window3()
        {
            InitializeComponent();
    
            // Запускаем диалог выбора библиотеки
            nameLibrary = SelectLibrary();
            // При отказе от выбора ничего не запускаем,
            // а в главном окне Window1 закрываем это окно 
            if (nameLibrary != String.Empty)
                ExecuteWindow();
        }
    
        // Извлекаем рисунки и управляем показом
        private void ExecuteWindow()
        {
            // Создаем файловый поток библиотеки
            fs = new FileStream(
                nameLibrary, FileMode.Open, FileAccess.Read, FileShare.Read);
    
            // Создаем читателя элементарных типов
            reader = new BinaryReader(fs);
            count = reader.ReadInt32();             // Возвращает int
            names = new String[count];
            for (int i = 0; i < count; i++)
                names[i] = reader.ReadString();
            fs.Seek(-sizeof(long), SeekOrigin.End); // Отступаем от конца
            offsetHead = reader.ReadInt64();        // Возвращает long
    
            // Привязываем массив имен рисунков к списку
            listBox.ItemsSource = names;
            // Выделяем первый элемент списка, чтобы вызвать SelectionChanged
            listBox.SelectedIndex = 0;
            listBox.Focus();
        }
    
        // При смене индекса списка показывает выбранный рисунок
        private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            /*
            // Вариант 1. Извлечение рисунка по индексу
            int index = ((ListBox)sender).SelectedIndex;
            ReadOnIndex(index);
            */
    
            // Вариант 2. Извлечение рисунка по имени
            string name = ((ListBox)sender).SelectedItem.ToString();
            ReadOnName(name);
        }
    
        // Вариант 1. Извлечение рисунка по индексу
        private void ReadOnIndex(int index)
        {
            // Локализуем рисунок по индексу
            if (index < 0 || index >= count) return;// Затычка
            fs.Seek(offsetHead + index * (sizeof(long) + sizeof(int)), SeekOrigin.Begin);
            long offset = reader.ReadInt64();   // Позиция рисунка в потоке (long)
            int length = reader.ReadInt32();    // Длина рисунка (int)
    
            // Извлекаем рисунок из потока
            byte[] bytes = new byte[length];
            //fs.Read(bytes, (int)offset, length);
            fs.Seek(offset, SeekOrigin.Begin);
            for (int i = 0; i < length; i++)
                bytes[i] = (byte)fs.ReadByte();
    
            // Отображаем рисунок пользователю
            BitmapImage bi = new BitmapImage();
            bi.BeginInit();
            bi.StreamSource = new MemoryStream(bytes);
            bi.EndInit();
            image.Source = bi;
        }
    
        // Вариант 2. Извлечение рисунка по имени
        private void ReadOnName(string name)
        {
            // По имени рисунка ищем его индекс в массиве names[]
            int index = -1;
            // Производится обход всего массива Array ПОДРЯД, 
            // начиная с первого элемента и заканчивая последним.
            // Одномерный поиск половинного деления, реализуемый
            // методом Array.BinarySearch() применять нельзя!!!
            String find = Array.Find<String>(names,
                delegate(String item)// Анонимный делегат
                {
                    index++;
                    // При первом совпадении заканчивает поиск
                    return String.Equals(item, name);
                });
            // В случае неудачи возвращает значение по умолчанию для типа шаблона
            if (find == String.Empty)   // Необязательно!
            {
                return;// Запрошено несуществующее имя
            }
    
            // А дальше в точности как в ReadOnIndex(), но вместо 
            // прямого вызова повторим для самостоятельности кода
            //ReadOnIndex(index);
            // Локализуем рисунок по индексу
            if (index < 0 || index >= count) return;// Затычка
            fs.Seek(offsetHead + index * (sizeof(long) + sizeof(int)), SeekOrigin.Begin);
            long offset = reader.ReadInt64();   // Позиция рисунка в потоке (long)
            int length = reader.ReadInt32();    // Длина рисунка (int)
    
            // Извлекаем рисунок из потока
            byte[] bytes = new byte[length];
            //fs.Read(bytes, (int)offset, length);
            fs.Seek(offset, SeekOrigin.Begin);
            for (int i = 0; i < length; i++)
                bytes[i] = (byte)fs.ReadByte();
    
            // Отображаем рисунок пользователю
            BitmapImage bi = new BitmapImage();
            bi.BeginInit();
            bi.StreamSource = new MemoryStream(bytes);
            bi.EndInit();
            image.Source = bi;
        }
    
        // Свойство доступа, проверяется в Window1
        public String NameLibrary   
        {
            get { return nameLibrary; }
        }
    
        // Диалог выбора библиотеки. Возвращает
        // пусто, если библиотека не выбрана
        private String SelectLibrary()
        {
            settings = new App7.Properties.Settings();
            String path = settings.dialogOpen;
            String fileName = IO.Path.Combine(path, "Pictures.my.lib");
              
            // Создаем и настраиваем диалог выбора файла
            FORMS.OpenFileDialog openFileDialog = new FORMS.OpenFileDialog();
            openFileDialog.InitialDirectory = path;
            openFileDialog.FileName = "";
            openFileDialog.Filter = "Library Files(*.my.lib)|*.my.lib";
            FORMS.DialogResult result = openFileDialog.ShowDialog();
            if (result != FORMS.DialogResult.Cancel)
                fileName = openFileDialog.FileName; // Полное имя
            else
                fileName = String.Empty;
    
            return fileName;
        }
    
        // Обработчик события Closing, срабатывает перед закрытием приложения
        private void Window_Closing(object sender, 
            System.ComponentModel.CancelEventArgs e)
        {
            // Закрываем библиотечный поток
            if (fs != null)
                fs.Close(); 
    
            // Сохраняем параметры 
            if (nameLibrary != String.Empty)
            {
                settings.dialogOpen = 
                    nameLibrary.Substring(0, nameLibrary.LastIndexOf('\\'));
                settings.Save();
            }
        }
    }
}
  • В панели Solution Explorer для проекта App7 раскройте узел Properties и двойным щелчком на файле Settings.settings откройте окно установки параметров, которое дополните еще одним параметром, как показано на рисунке

  • Запустите приложение - выберите ранее созданную библиотеку Pictures.my.lib - результат соответствует ожидаемому

  • Разберитесь с кодом примера, который содержит подробные комментарии

Пример 4. Создание файловой библиотеки рисунков с сохранением метаданных в OLE DB

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

В этом примере поставим задачу 'отделить зерна от плевел' - рисунки хранить по прежнему в библиотечном файле, а метаданные разместить в базе данных типа OLE DB (тоже в файле, только 'по турецки'). Это плохая идея и мы ее позже исправим, но пока для тренировки это будет очень даже ничего. Известно, что БД представляет собой набор структурированных данных, поддерживаемый специальным API ( Application Programming Interface ), поэтому записывать и извлекать нужные сведения об основных данных нам будет гораздо легче. А насчет 'быстрее' - вопрос спорный, если не использовать пул ( pool ) соединений.

Правда, классики могут взвизгнуть - опять 'обрывки каких-то материалов', не относящихся к теме потоков. Мол, "Хотели кока, а съели Кука!" (Высоцкий В.С.). Успокойтесь, "течет речечка по песочечку - золотишко моет!": тема остается, и ее более широкое название - программирование, а не Кама-Сутра или философия! Чем более рельефным будет изложение, тем больше извилин останется в голове, да и руки быстрее выпрямятся. При гладком повествовании со временем можно легко забыть не только начало, но и Мать родную (и ведь забывают...), в крутых поворотах жизни и загогулинах - все вспомнишь!

Я не призываю слепо следовать за мной, а рекомендую выполнить и изучить: "делай как я, а потом и свое подоспеет". Непонятные места готового кода всегда можно выяснить в документации и на форумах. Главное - осмысливать, искать ( студент - ищущий знаний) и не падать духом - все придет с тренировкой.

В решении поставленной задачи нужно выполнить 'совсем немного':

  1. Создать саму базу данных
  2. Упаковать рисунки в файл, а метаданные сохранить в БД
  3. Запрашивать данные из соответствующих источников и показывать их на экране

Начнем с создания реляционной БД программным способом. Выберем наиболее простую - файловую БД типа Access, позволяющую использовать интерфейсы OLE D B ( Object Linking and Embedding, Databas e ). Нам необходимо определить ее структуру, включающую в себя хотя бы одну таблицу (одна и нужна). База данных будет иметь имя Pictures.my1.md b и содержать одну таблицу с именем MyTable (вначале назвал Local, а оказалось, что это служебное имя в API, - ох и помучился с отладкой!). Структура таблицы в типах OLE D B и объектной модели C # будет такой

Таблица 19.5. Структура таблицы MyTable
Имя поля Тип OLE DB Тип объектной модели C# Размер
Name Type Type DefinedSize
FileName (уникальное) adWChar string 20 (определим сами, с запасом)
Offset adInteger int 4 (предопределено)
Length adInteger int 4 (предопределено)

Целый тип adInteger имеет длину 4 байта и может адресовать 4Б = 256*256*256*127 = 2'130'706'432, т.е. примерно 2 гигабайта памяти, что для библиотеки разумной длины вполне достаточно. Интересно отметить, что в Office Access он называется длинным целым. Эта терминология соответствует 'старым' временам (когда деревья были большими...), когда long имел длину 4 байта, а int - всего 2 байта.

У внимательного читателя при знакомстве с приведенной таблицей может возникнуть справедливый вопрос. Как так, переменная Offset в методе сдвига Seek() должна быть знаковой, соответствовать типу long C# и иметь длину 8 байт, и почему это вдруг нам подсовывают тип adInteger? Действительно, в API программного создания структуры таблиц БД, который мы в дальнейшем будем использовать, существует требуемый числовой тип и именуется он adBigInt. Но компилятор с ним почему-то упорно не хочет работать.

Можно было бы пойти обходным путем: поле Offset объявить строкой длиной 8 символов (или более) и выполнять двухстороннюю конвертацию типов String/Int64 при каждой операции чтения/записи между полем таблицы и объектной моделью. Можно придумать и другие ухищрения. Но для учебного примера пусть все останется так как есть - мы не собираемся создавать файловые библиотеки тяжелея 2 Гб. Да и вообще, то что мы здесь делаем вместе, - это только иллюстрация возможностей и тренировка. Все остальное вы сделаете сами в реальной взрослой работе на производстве, но уже будучи вооруженными (и очень опасными!).

  • В файле Window1.xaml измените заголовок списка для элемента ' Пример 4 ' на ' Пример 4. Создание БД + файловой библиотеки '

База данных типа Access не требует установки на клиенте SQL -сервера. Создавать БД и таблицу в ней будем программно с помощью COM -объектов библиотек ADOX.dll и ADODB.dll. Управление данными в БД выполним с помощью классов ADO.NET.

  • В Solution Explorer выделите узел проекта App7 и добавьте к нему командой меню оболочки Project/Add Reference из вкладки COM ссылки на библиотеки ADOX.dll и ADODB.dll последних версий, как показано на снимке

Оболочка добавит в каталог ссылок проекта App7 две библиотеки


Обратите внимание, что библиотека System.Data.dll, в которой находятся классы ADO.NET, уже подключена изначально мастером создания проектов. Почти в любом типе проекта предусмотрено автоматическое подключение System.Data.dll, что свидетельствует о популярности задач с использованием баз данных.

  • В панели Object Browser установите выпадающий список Browse в значение текущего решения и убедитесь, что классы COM -библиотек ADOX.dll и ADODB.dll упакованы в пространствах имен ADODB и ADOX. Эти префиксы мы и будем использовать в API перед именами классов при создании БД

  • В панели Solution Explorer добавьте к узлу App7 командой контекстного меню Add/Class новый файл с именем CreateLibraryAndDB.cs

Конечно же, к этому времени класс мною уже написан и отлажен, и я мог бы 'вывалить' его здесь сразу целиком. Но опять же, классики завизжат про непедагогичность, про то, что даже при обильном словоблудии рядом с 'непьющим' студентом должен стоять родитель с ремнем (или с ружьем). Поэтому выльем, но постепенно. Все равно 'пьющие' будут разбираться именно в 'живом' коде, а там комментариев хватает (как и вариантов).

А, пусть визжат, вываливаю целиком, надоело описывать!

  • Заполните файл CreateLibraryAndDB.cs следующим кодом (приводится полностью)
using System;
using System.Windows.Forms;
using System.IO;
    
// Пространства имен для ADO.NET
using System.Data;
using System.Data.OleDb;
using System.Data.Common;
    
namespace App7
{
    class CreateLibraryAndDB
    {
        // Локальные поля
        String nameDB = "Pictures.my1.mdb",
            nameLibrary = "Pictures.my1.lib";
        string path, fileName,
            tableName = "MyTable";// Нельзя давать имя "Local"!!!
        String extensionPictures = "jpg";
    
        // Параметризованный конструктор
        public CreateLibraryAndDB(string path)
        {
            // Инициализируем поля
            this.path = path;
            fileName = Path.Combine(path, nameDB);
    
            // Удалим предыдущую базу данных, если она есть
            if (new FileInfo(fileName).Exists)
                File.Delete(fileName);
        }
    
        // Конструктор по умолчанию. Необязательно!
        public CreateLibraryAndDB() { }
    
        // Строка соединения с БД
        private String ConnectionString(String fileName)
        {
            //string pathToFile = Application.StartupPath.ToString();
            string JetEngineString = "Provider=Microsoft.Jet.OLEDB.4.0;" +
                "Jet OLEDB:Engine Type=5;Data Source=";
            return JetEngineString + fileName;
        }
    
        // Создание БД
        public bool CreateDatabase()
        {
            Object adoConnection = null;
            ADOX.CatalogClass catalog = new ADOX.CatalogClass();
    
            try
            {
                catalog.Create(ConnectionString(fileName));
                adoConnection = catalog.ActiveConnection;
            }
            catch (Exception ex)
            {
                MessageBox.Show(String.Format("Ошибка в создании БД\n{0}", ex.Message),
                    "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }
            finally
            {
                if (catalog != null && adoConnection != null)
                {
                    ((ADODB.Connection)adoConnection).Close();// Приводим к интерфейсу
                    /***********  Вариант  *********
                    adoConnection.GetType().InvokeMember(
                        "Close", System.Reflection.BindingFlags.InvokeMethod,
                        null, adoConnection, new object[0]);
                    //*****************************/
    
                    catalog = null;
                    adoConnection = null;
                }
            }
    
            return true;// Успех, если дошли до этого места
        }
    
        // Создание таблицы
        public bool CreateTable()
        {
            ADODB.Connection conn = new ADODB.ConnectionClass();
            conn.Open(ConnectionString(fileName), null, null, 0);
            ADOX.Catalog catalog = new ADOX.CatalogClass();
            catalog.ActiveConnection = conn;
    
            try
            {
                ADOX.TableClass table = new ADOX.TableClass();// Объект таблицы
                table.ParentCatalog = catalog;  // Указали родителя для таблицы
                table.Name = tableName;         // Имя таблицы
                catalog.Tables.Append(table);   // Добавили в коллекцию таблиц
    
                // Задаем параметры настройки таблицы
                string[] columnNames =
                {
                    "FileName",
                    "Offset",
                    "Length"
                };
                // Для adInteger размер предопределен, можно заменить нулями
                int[] columnSizes = new int[] { 20, 0, 0 };
                ADOX.DataTypeEnum[] columnTypes = new ADOX.DataTypeEnum[] 
                    {
                        ADOX.DataTypeEnum.adWChar,      // До 255 символов fixed
                        ADOX.DataTypeEnum.adInteger,    // adBigInt = 8Б знаковое, но не идет
                        ADOX.DataTypeEnum.adInteger     // 4Б = 256*256*256*127 = 2'130'706'432
                    };
    
                // Настраиваем структуру таблицы 
                ADOX.ColumnClass column;
                for (int i = 0; i < columnSizes.Length; i++)
                {
                    column = new ADOX.ColumnClass();
                    column.ParentCatalog = catalog;  // Указали родителя для столбца
                    column.Name = columnNames[i];
                    column.DefinedSize = columnSizes[i];
                    column.Type = columnTypes[i];
                    column.Attributes = ADOX.ColumnAttributesEnum.adColNullable;// Позволить пусто
                    column.Attributes = ADOX.ColumnAttributesEnum.adColFixed;// Фиксированная длина
                    table.Columns.Append(column, column.Type, column.DefinedSize);
                }
    
                // Делаем первый столбец уникальным (совпадения не допускаются)
                // http://www.sqldev.org/sql-server-data-access/
                // multi-column-primary-keys-in-access-using-adox-c-60766.shtml
                ADOX.Table tbl = catalog.Tables[tableName];
                tbl.Keys.Append(
                    "UniqueKeyLocal", ADOX.KeyTypeEnum.adKeyUnique, "FileName", null, null);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
                return false;
            }
            finally
            {
                conn.Close();// Приводим к интерфейсу
            }
    
            return true;// Успех, если дошли до этого места
        }
    
        // Создаем файловую библиотеку и наполняем БД данными
        public bool CreateData()
        {
            OleDbConnection conn = new OleDbConnection(ConnectionString(fileName));
            OleDbDataAdapter adapter = new OleDbDataAdapter(
                new OleDbCommand("SELECT * FROM " + tableName, conn));
            // Загружаем данные БД в объектную модель таблицы
            DataTable table = new DataTable(tableName);
            // Заполняем объектную таблицу данными из БД (хоть и нет пока данных), 
            // но лучше ограничиться чтением схемы, если собираемся только добавлять
            adapter.Fill(table);
            /***********  Вариант  *********
            adapter.FillSchema(table, SchemaType.Source);
            //*****************************/
    
            // Читаем информацию о всех файлах выбранного каталога
            FileInfo[] filesInfo = new DirectoryInfo(path).
                GetFiles("*." + extensionPictures);
            if (filesInfo.Length == 0)
            {
                MessageBox.Show(
                    "Нет в выбранном каталоге файлов для упаковки",
                    "Предупреждение",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Information);
                return false;
            }
    
            // Рисунки пишем в библиотеку, а их параметры - в объектную таблицу 
            FileStream fs = new FileStream(
                Path.Combine(path, nameLibrary), FileMode.Create);
            using (fs)
            {
                for (int i = 0; i < filesInfo.Length; i++)
                {
                    // Получаем сокращенное имя файла без пути и расширения
                    String shortName = filesInfo[i].Name; // Уже без пути и есть!!!
                    int len = shortName.LastIndexOf('.');
                    shortName = shortName.Substring(0, len);// Убрали расширение
    
                    // Добавляем в объектную таблицу очередную строку
                    DataRow row = table.NewRow();// Новая строка таблицы
                    row["FileName"] = shortName;
                    row["Offset"] = (int)fs.Position;
                    row["Length"] = (int)filesInfo[i].Length;
                    table.Rows.Add(row);
                    //table.AcceptChanges();// Нельзя, изменения будут считаться принятыми!!!
    
                    // Читаем файл с рисунком и добавляем в библиотеку
                    FileStream fileStream = filesInfo[i].OpenRead();
                    int readByte;
                    while ((readByte = fileStream.ReadByte()) != -1)
                        fs.WriteByte((byte)readByte);
                    /***********  Вариант  *********
                    byte[] bytes = File.ReadAllBytes(
                        Path.Combine(filesInfo[i].DirectoryName, filesInfo[i].Name));
                    MemoryStream pictureStream = new MemoryStream(bytes);
                    pictureStream.WriteTo(fs);
                    pictureStream.Close();
                    //*****************************/
                }
    
                fs.Flush(); // Необязательно! Сбросится при закрытии потока 
            }
    
            // Записываем объектную таблицу в таблицу OLE DB
            try
            {
                // Создаем построитель команд по настройке свойства SelectCommand
                // адаптера. Адаптер сам сгенерирует нужные команды при обновлении
                OleDbCommandBuilder commandBuilder =
                    new OleDbCommandBuilder(adapter);
                /***********  Вариант  *********
                OleDbCommandBuilder commandBuilder = 
                    new OleDbCommandBuilder();
                adapter.InsertCommand = commandBuilder.GetInsertCommand();
                adapter.DeleteCommand = commandBuilder.GetDeleteCommand();
                adapter.UpdateCommand = commandBuilder.GetUpdateCommand();
                //*****************************/
    
                // Адаптер сам откроет соединение, сам его закроет и сам пометит
                // объектную таблицу методом table.AcceptChanges() - изменения приняты
                adapter.Update(table);
    
                // Пометить как удаленный объект
                //table.Clear();
                //table.Dispose();
            }
            catch (DataException ex)
            {
                MessageBox.Show(ex.Message);
                return false;
            }
            finally
            {
                conn.Close();
            }
    
            return true;// Успех, если дошли до этого места
        }
    
        /*  http://yandex.ru/yandsearch?p=1&text=ADOX.CatalogClass&clid=14586
        Byte[] getResource(String name)
        {
            BinaryReader br = new BinaryReader(this.GetType().Assembly.GetManifestResourceStream(name));
    
            Byte[] bytes = br.ReadBytes((int)br.BaseStream.Length);
            br.Close();
            return bytes;
        }
        */
    }
}

Построитель команд OleDbCommandBuilder может создавать команды обновления только для одной таблицы БД. При более изощренных ручных настройках адаптера данные для одной объектной таблицы можно собирать из разных таблиц БД и, соответственно, обновлять во многих. Но в этом случае OleDbCommandBuilder применять нельзя. Кроме того, он правильно работает только тогда, когда в загруженной таблице БД имеются ключевой или уникальный столбцы. Поэтому в функции CreateTable() при создании таблицы БД первый столбец мы назначили уникальным. Это правомерно, поскольку все равно в одном каталоге не может быть несколько одноименных файлов, а мы собираем рисунки из одного каталога.

Столбцы, помеченные атрибутом adColNullable, могут быть не заполнены значениями и оставаться пустыми, что мы на всякий случай и предусмотрели. Экземпляры классов ADOX и ADODB могут адресоваться ссылками типа класса или наследуемого интерфейса, это мы тоже использовали в коде.

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

Оба метода сами открывают соединение с БД и сами закрывают. Но если принудительно открыть соединение методом Open() объекта соединения, то упомянутые методы его не закроют и нужно явно вызывать метод Close() объекта соединения. Открытие соединения - медленная операция, но она вполне оправдана. С коллективным доступом к одной и той же БД оптимальным является отсоединенный режим, при котором доступ к БД открывается кратковременно на время загрузки или обновления данных. После чего немедленно приходится закрывать соединение, иначе база останется блокированной текущим пользователем.

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

  • В файле Window1.xaml.cs найдите функцию Example4() и заполните ее следующим вызывающим кодом (приводится полностью)
void Example4()
        {
            // Диалог выбора каталога
            dialogFolder = 
                new System.Windows.Forms.FolderBrowserDialog();
            dialogFolder.Description = "Выберите каталог для создания БД";
            dialogFolder.ShowNewFolderButton = false;// Отключить кнопку создания новой папки
            dialogFolder.RootFolder = Environment.SpecialFolder.MyComputer;// Компьютер
            dialogFolder.SelectedPath = settings.dialogFolder;
            System.Windows.Forms.DialogResult result = dialogFolder.ShowDialog();
            // Если был отказ или диалог закрыт системной кнопкой
            if (result == System.Windows.Forms.DialogResult.Cancel)
                return;
            
            // Извлекаем новый каталог
            String path = dialogFolder.SelectedPath;// Полный путь
            // Создаем структуру БД
            CreateLibraryAndDB create = new CreateLibraryAndDB(path);
            bool success;
            if (success = create.CreateDatabase())
                success = create.CreateTable();
            if (!success)
            {
                MessageBox.Show("Структура БД не создана");
                return;
            }
    
            success = create.CreateData();
            if (success)
                MessageBox.Show("Файловая библиотека и база данных созданы");
            else
                MessageBox.Show("Файловая библиотека и база данных не созданы");
    
            // Принудительная перерисовка окна WPF
            this.Height = this.ActualHeight + 1;
            this.Height = this.ActualHeight - 1;
        }

Здесь мы используем параметры того же диалога выбора каталога, каторый применяли ранее при создании файловой библиотеки. Возвращаемые функциями флаги позволяют вовремя прервать последовательное выполнение, если предыдущий этап закончился неудачей. Принудительная перерисовка окна WPF немного компенсирует недостатки этой новой технологии, поскольку после закрытия заслоняющего окна сообщения само WPF -окно почему-то не перерисовывается и остается размазанным.

  • Запустите пример и зайдите в папку Images, расположенную в прилагаемом к работе каталоге Source

Мы видим, что библиотечный файл Pictures.my1.lib и файловая БД Pictures.my1.mdb создаются и наполняются данными. Если открыть нашу БД в Office Access, то она будет такой


Пока вроде бы все складывается так, как задумали. Мы получили какие-то результаты. Но правильные ли они? Чтобы уверенно это заявить, придется создавать еще один код, отображающий рисунки пользователю, как мы это делали после создания файловой библиотеки. А это опять программирование - т.е. опять именно то, что нам и нужно.

< Лекция 5 || Лекция 6: 12345678910111213
Алексей Бабушкин
Алексей Бабушкин

При выполнении в лабораторной работе упражнения №1 , а именно при выполнении нижеследующего кода:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using Microsoft.Xna.Framework.Graphics;

   

namespace Application1

{

    public partial class MainForm : Form

    {

        // Объявим поле графического устройства для видимости в методах

        GraphicsDevice device;

   

        public MainForm()

        {

            InitializeComponent();

   

            // Подпишемся на событие Load формы

            this.Load += new EventHandler(MainForm_Load);

   

            // Попишемся на событие FormClosed формы

            this.FormClosed += new FormClosedEventHandler(MainForm_FormClosed);

        }

   

        void MainForm_FormClosed(object sender, FormClosedEventArgs e)

        {

            //  Удаляем (освобождаем) устройство

            device.Dispose();

            // На всякий случай присваиваем ссылке на устройство значение null

            device = null;       

        }

   

        void MainForm_Load(object sender, EventArgs e)

        {

            // Создаем объект представления для настройки графического устройства

            PresentationParameters presentParams = new PresentationParameters();

            // Настраиваем объект представления через его свойства

            presentParams.IsFullScreen = false; // Включаем оконный режим

            presentParams.BackBufferCount = 1;  // Включаем задний буфер

                                                // для двойной буферизации

            // Переключение переднего и заднего буферов

            // должно осуществляться с максимальной эффективностью

            presentParams.SwapEffect = SwapEffect.Discard;

            // Устанавливаем размеры заднего буфера по клиентской области окна формы

            presentParams.BackBufferWidth = this.ClientSize.Width;

            presentParams.BackBufferHeight = this.ClientSize.Height;

   

            // Создадим графическое устройство с заданными настройками

            device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Hardware,

                this.Handle, presentParams);

        }

   

        protected override void OnPaint(PaintEventArgs e)

        {

            device.Clear(Microsoft.Xna.Framework.Graphics.Color.CornflowerBlue);

   

            base.OnPaint(e);

        }

    }

}

Выбрасывается исключение:

Невозможно загрузить файл или сборку "Microsoft.Xna.Framework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=6d5c3888ef60e27d" или один из зависимых от них компонентов. Не удается найти указанный файл.

Делаю все пунктуально. В чем может быть проблема?