Интересное обсуждение у меня сложилось на форуме UniGUI, когда я не смог передать ссылку на объект так как я это делаю обычно, в Delphi (через свойства или через параметр Sender). Вот ссылка на само обсуждение
http://forums.unigui.com/index.php?/topic/7556-how-to-give-correct-link-to-created-instance
Итак, в UniGUI есть 3 типа объектов
–UniServer Module ( создается 1 раз и работает все время, пока работает приложение)
http://www.unigui.com/doc/online_help/server_module.htm
–UniMainModule (создается 1 раз и работает в течение сессии – о том, что такое сессия в UniGUI можно узнать в другом моем посте – и это не тоже самое, что сессия в PHP )
http://www.unigui.com/doc/online_help/main_module.htm
–Все остальное – работает от создания до закрытия. Например формы. Создали – она держится в памяти – закрыли – уносит все данные с собой. В этом смысле UniMainModule это вроде как глобальный юнит, к которому можно обратиться, например как UniMainModule.doSomething, и также к UniServerModule.doSomething.
Как выяснилось на форуме, данные форм лучше хранить в зеркальных структурах в UniMainModule, так как просто этот объект живет дольше и не убивается при закрытии формы.
Разберем пример соединения с базой данных
Я сразу покажу как правильно с точки зрения автора фрэймворка Farshad Mojaheri и почему так правильно. Ну а далее вы можете сделать как Вам удобно. Итак, для соединения с БД будем использовать технологию FireDAC.
Итак, у нас имеется MySQL, FireDAC, Delphi XE Seattle
Проблема 1 – где хранить libmysql.dll и как программе показывать где находится библиотека?
Как известно, для работы с БД MySQL из Delphi нужен файл libmysql.dll.
Также известно, что FireDAC поддерживает 3 типа соединений
-Persistent
-Private
-Temporary
Более подробно про виды соединений в FireDAC здесь
Также известно, что UniGUI приложение можно откомпилировать как EXE и как DLL для запуска на IIS.
Соответственно, когда у нас EXE, то libmysql.dll можно положить просто в папку с EXE и забыть о том, что приложение будет искать этот файл, так как перво наперво приложение проверит именно свою же папку, в которой оно находится.
А вот случай с DLL интереснее. Тут уже непонятно куда положить файл libmysql.dll, так, чтобы приложение его нашло. Можно пробовать в С:\Windows\System32 или c:\windows\SysWow64\
Но это такой путь, не универсальный, где-то он сработает а где-то нет. Мне хотелось научиться делать это более менее универсально. То есть хранить файлы в произвольной папке, скажем в C:\MySQLDriver и просто показывать приложению, что libmysql.dll нужно искать именно там. Ну, кто ищет, тот найдет. И вот оказывается в FireDAC есть такая замечательная вещь как Persistent и Private подключения, которые пользуются файлами
FDConnectionDefs.ini – хранит в себе настройки подключения – имя БД, пароль, сервер, порт и др.
FDDrivers.ini – хранит в себе имена и пути до драйверов
Итак, что можно сделать? В FDConnectionDefs.ini добавить набор настроек, например так
1 2 3 4 5 6 7 8 9 10 |
... [MySQL_Persistent_def] DriverID=MySQL327 Database=smsmessendger_db User_Name=root Password=masterkey Server=127.0.0.1 Port=3306 CharacterSet=utf8 ... |
В FDDrivers.ini сделать виртуальный драйвер
1 2 3 4 5 6 |
... [MySQL327] ; MySQL327 virtual driver will use specified LIBMYSQL.DLL BaseDriverID=MySQL VendorLib=C:\MySQLDriver\libmysql.dll ... |
И такой подход полностью избавит от зависимости от хранения libmysql.dll в папке проекта, и тогда в случае компиляции UniGUI проекта в DLL – можно будет просто указать в качестве папки для хранения Libmysql.dll папку C:\MySQLDriver
Проблема 2 – где хранить FDManager?
Чтобы указывать приложению где находятся файлы FDConnectionDefs.ini и FDDrivers.ini нужен компонент FDManager, в свойствах которого мы прописываем пути к соответствующим файлам.
Сначала я пробовал хранить этот компонент в uDBConnection модуле, но при запуске приложение ругалось на то, что только 1 FD Manager должен быть на все приложение. В результате обсуждения на форуме пришли к выводу, что лучше его хранить на сервере. Когда я перенес его на серверную часть – все встало на свои места.
Проблема № 3 – как подключаться?
Здесь я приведу 2 метода – первый подключает приложение к базе данных, если libmysql.dll лежит в папке с проектом, второй же – если libmysql.dll лежит в папке, определенной в FDDrivers.ini, который в свою очередь используется в одном из определений файла FDConnectionDefs.ini
Итак, метод первый – для случая, если libmysql.dll лежит в той же папке что и EXE файл
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
function TDBConnection.Connect: boolean; var oParams: TStrings; ErrorInfo:string; begin oParams:=TStringList.Create; try oParams.Add('DataBase=smsmessendger_db'); oParams.Add('Password=masterkey'); oParams.Add('User_Name=root'); oParams.Add('Port=3306'); oParams.Add('Server=localhost'); oParams.Add('CharacterSet=utf8'); FDConnection.Params.Assign(oParams); FDConnection.DriverName:='MySQL'; //Пробуем подключиться try FDConnection.Connected:=true; if FDConnection.Connected then begin Result:=true; //showmessage('Connected'); end else Result:=false; except on E: EFDDBEngineException do case E.Kind of ekUserPwdInvalid: // user name or password are incorrect raise Exception.Create('DBConnection Error. User name or password are incorrect'+ #13#10+#13#10+E.ClassName+' поднята ошибка, с сообщением : '+E.Message); ekUserPwdExpired: raise Exception.Create('DBConnection Error. User password is expired' +#13#10+#13#10+E.ClassName+' поднята ошибка, с сообщением : '+E.Message); ekServerGone: raise Exception.Create('DBConnection Error. DBMS is not accessible due to some reason' +#13#10+#13#10+E.ClassName+' поднята ошибка, с сообщением : '+E.Message); else // other issues raise Exception.Create('DBConnection Error. UnknownMistake' +#13#10+#13#10+E.ClassName+' поднята ошибка, с сообщением : '+E.Message); end; on E : Exception do raise Exception.Create( E.ClassName+' поднята ошибка, с сообщением : '+#13#10+#13#10+E.Message ); end; finally FreeAndNil(oParams); end; end; |
Метод 2 для случая, если файл libmysql.dll лежит в произвольной папке
Для начала создаем определение соединения
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
procedure TDBConnection.AddConnectionDef(Sender: TObject); var myDef: IFDStanConnectionDef; i:integer; begin with UniMainModule do begin //Проверяем нет ли уже ConnDef с таким же именем for i := 0 to FDManager.ConnectionDefs.Count-1 do begin if FDManager.ConnectionDefs[i].Name='MySQL_Persistent_def' then Exit; end; //Далее, добавляем, если ConnDef c именем MySQL_Persistent_def отсутствует myDef:=FDManager.ConnectionDefs.AddConnectionDef; myDef.Name:='MySQL_Persistent_def'; myDef.Params.DriverID:='MySQL'; myDef.Params.Database:='smsmessendger_db'; myDef.Params.UserName:='root'; myDef.Params.Password:='masterkey'; // Далее другая техника добавления параметров myDef.Params.Add('Server=127.0.0.1'); // << я не нашел в свойствах Server, поэтому добавил так myDef.Params.Add('Port=3306'); myDef.Params.Add('CharacterSet=utf8'); // Отмечаем myDef как Persistent, без этого был бы Private myDef.MarkPersistent; // Сохраняем изменения в файле FDConnectionDefs.ini myDef.Apply; end; end; |
Далее собственно пробуем соединяться
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
function TDBConnection.Connect2: boolean; begin try Self.AddConnectionDef(Self); // Adding connection def... FDConnection.ConnectionDefName:='MySQL_Persistent_def'; FDConnection.Connected:=true; if FDConnection.Connected then begin Result:=true; //showmessage('Connected'); end else Result:=false; except on E: EFDDBEngineException do case E.Kind of ekUserPwdInvalid: // user name or password are incorrect raise Exception.Create('DBConnection Error. User name or password are incorrect'+ #13#10+#13#10+E.ClassName+' поднята ошибка, с сообщением : '+E.Message); ekUserPwdExpired: raise Exception.Create('DBConnection Error. User password is expired' +#13#10+#13#10+E.ClassName+' поднята ошибка, с сообщением : '+E.Message); ekServerGone: raise Exception.Create('DBConnection Error. DBMS is not accessible due to some reason' +#13#10+#13#10+E.ClassName+' поднята ошибка, с сообщением : '+E.Message); else // other issues raise Exception.Create('DBConnection Error. UnknownMistake' +#13#10+#13#10+E.ClassName+' поднята ошибка, с сообщением : '+E.Message); end; on E : Exception do raise Exception.Create( E.ClassName+' поднята ошибка, с сообщением : '+#13#10+#13#10+E.Message ); end; end; |
Проблема 4 – где хранить остальные элементы подключения?
Вариант 1 – через Free DataModule и создание экземпляра класса в UniMainModule
Как известно, Free DataModule в UniGUI – это аналог традиционного TDataModule в Delphi. Здесь ручное управление памятью. Сами его создали и сами можем уничтожить.
Итак, нам потребуется отдельный модуль uDBConnection с методами подключения и следующими компонентами
Экземпляр TDBConnection, определенного в этом модуле можно создать в MainForm, или в какой-нибудь Form, но нужно понимать, что тогда это соединение уничтожится вместе с закрытием формы.
Лучшим местом для создания экземпляра класса будет UniMainModule – так как данный модуль, сам по себе работает на протяжении сессии в UniGUI, то есть, например так…
1 2 3 4 5 |
procedure TUniMainModule.UniGUIMainModuleCreate(Sender: TObject); begin FDBConnection:=TDBConnection.Create(UniApplication); FDBConnection.Connect2; end; |
Единственный момент – если произошла какая-то ошибка – нигде об этом не сообщается. По крайней мере этого не видно, но в случае создания соединение в MainForm, об ошибке будет сообщено в браузере.
Вариант 2 – через Application DataModule
Этот дата модуль создается автоматически вместе с сессией и уничтожается, чтобы его использовать в UniMainModule нужно добавить в его uses ссылку на наш модуль uDBConnection
1 2 |
uses uDBConnection |
И тогда не нужно ничего создавать и уничтожать, из любого модуля сможем обратиться как
1 |
UniMainModule.DBConnection.Connect |
Что дальше? Тестируем работу приложения.
Для теста я сделал вот такой простой код, который повесил на кнопку
1 2 3 4 5 |
... if UniMainModule.DBConnection.FDConnection.Connected then ShowMessage('Ok') else ShowMessage('not Ok'); ... |