Некоторое время назад (уже уж более полугода) я устроился на работу в отличную фирму в группу разработки мобильных решений на платформе MS Windows CE и библиотеки .NET Compact Framework (.NET CF) в среде Visual Studio 2008. Первое время я занимался поддержкой существующих приложений либо разрабатывал настольные приложения различного назначения.
Мобильные решения часто в составе имеют также базу данных SQL Server Compact (SQL Server CE) в виде единого файла *.sdf, который хранится либо во внутренней памяти мобильного устройства (и, соответственно, удаляется при горячей перезагрузке), либо на флеш-карте, если такая есть в устройстве (так как флеш-память энергонезависима, файл базы данных остается после любой перезагрузки). В связи с этим возникает задача создания пустой базы данных по шаблону, в которой приложение может складировать данные. Такая может быть заполнена по умолчанию какими-либо данными (например, можно хранить номер версии базы для поддержания ее в актульном состоянии). У меня была возможность сравнить два решения этой задачи, которыми я готов поделиться.
Обращаю внимание на то, что такая задача редко возникает в настольных приложениях, так как в них обычно идет подключение к уже существующей базе данных на сервере. Но в случае использования локальной БД, где ее требуется создавать, задача приобретает похожий смысл.
То, как было придумано до меня
В тех проектах, которые я поддерживаю, задача создания пустой базы решается следующим образом:
- из кода создается база данных в указанном месте (во внутренней память или на флешке):
// useInsideStore - хранить ли базу во внутренней памяти string path = useInsideStore ? "\\temp\\MyDatabase.sdf" : "\\storage card\\MyDatabase.sdf"; if (!File.Exists(path)) { try { SqlCeEngine engine = new SqlCeEngine(connectionString); engine.CreateDatabase(); } finally { engine.Dispose(); } }
- из кода вызываются команды на создание таблиц:
SqlCeCommand command = _connection.CreateCommand(); command.CommandText = "CREATE TABLE [Users] (" + "Id int PRIMARY KEY, " + "UserName nvarchar(50) NOT NULL)"; command.ExecuteNonQuery();
- в коде выполняются запросы на добавление некоторых данных по умолчанию:
Решение имеет право на существование и оно работает. Но, лично для меня, такое решение кажется неудобным в первую очередь из-за того, что такой "шаблон" базы тяжело поддерживать.
Представим, что появилась необходимость в какой-то таблице базы изменить/добавить столбец с данными. Для этого придется в коде запроса на создание соответствующей таблицы вносить изменения. Кому-то покажется, что это тривиальная задача и, в общем-то, ничего сложного в этом нет, и он будет прав. А я вот, лично, редко пишу запросы на создание и изменение структуры БД, поэтому, чтобы выполнить эту операцию, придется идти читать документацию, тратить время и т.д. Да и где гарантия, что я сделаю все правильно с первого раза? А если нужно добавить не столбец, а новую таблицу? В общем, думаю, мысль понятна.
Таким образом, могу выделить минусы такого решения:
- возможность допустить ошибку при создании/редактровании столбца/таблицы/прочее;
- относительно большие затраты по времени;
- и еще один минус, который не может пройти мимо, в процессе design-time фактически нет экземпляра БД.
То, что пришло мне в голову
Недавно передо мной встала задача разработки нового приложения. И тут у меня появилась возможность решить задачу создания пустой базы иным способом.
Во-первых, мне кажется логичным, чтобы база была создана в design-time, с нужным набором данных и с которой можно было работать средствами, например, Visual Studio, - иметь своего рода "болванку".
Во-вторых, хочется, чтобы файл базы данных входил в состав проекта Visual Studio и коммитился в svn вместе со проектом.
В принципе, можно так и сделать. В свойствах проекта через Add -> New Item создаем файл базы данных:
Далее появляется диалог для создания типизированного DataSet, но если нужды в нем нет, то от такой услуги можно спокойно отказаться.
Затем с помощью средств самой студии (в окне Server Explorer) можно создать необходимые таблицы и наполнить их необходимыми данными.
В принципе, на этом можно было бы остановиться, поставив в свойствах созданного файла базы данных в параметре Copy to Output Directory значение Copy always. Таким образом, рядом со сборками при построении проекта будет валяться "болванка" базы.
А далее при необходимости шаблонную базу можно копировать в любое место.
Бонус
Все было бы так просто, если бы не одно НО! В отличие от первого решения, у нас в результате появился дополнительный файл. Ничего плохого в этом нет, но и ничего хорошего тоже. Мало того, что эталонный файл могут случайно удалить пользователи, подумав, что база лежит в другом месте (во внутренней памяти или на флешке, куда они сами указали в настройках приложения), будет еще хуже, если они его начнут править.
В общем, такой вариант меня лично не устраивает. Поэтому делаем откат до того момента, как задали значение параметру Copy to Output Directory и оставляем его по умолчанию - Do not copy. Напротив, параметру Build Action устанавливаем значение Embedded Resource.
Что нам это дало? А то, что файл базы данных теперь будет входить в состав сборки. А копировать файл базы в указанное место можно, например, следующим образом:
// useInsideStore - хранить ли базу во внутренней памяти string path = useInsideStore ? "\\temp\\MyDatabase.sdf" : "\\storage card\\MyDatabase.sdf"; if (!File.Exists(path)) { FileStream fileStream; byte[] bytes; using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("YourNamespace.MyDatabase.sdf")) { fileStream = File.OpenWrite(path); BinaryReader binaryReader = new BinaryReader(stream); bytes = binaryReader.ReadBytes((int) stream.Length); } fileStream.Write(bytes, 0, bytes.Length); fileStream.Close(); }
Вместо YourNamespace нужно подставить пространство имен, в котором сохранен файл базы данных.
Такое решение лишено недостатков первого варианта. Конечно, появился другой недостаток - размер сборки увеличивается на размер "болванки" базы. Но, по-моему, эта та цена, которую во многих случаях можно и заплатить.
Спасибо за внимание! Если у вас есть другие решения такой задачи, с удовольствием пообсуждаем их в комментариях.
Ссылки по теме:
Комментариев нет:
Отправить комментарий