perlon Opublikowano 13 Listopada 2018 Zgłoś Udostępnij Opublikowano 13 Listopada 2018 A coś free bez watermark'a Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Parikon Opublikowano 13 Listopada 2018 Autor Zgłoś Udostępnij Opublikowano 13 Listopada 2018 Dlaczego mamy przejść na System.Windows.Forms zamiast System.Windows? Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
kruszynski Opublikowano 13 Listopada 2018 Zgłoś Udostępnij Opublikowano 13 Listopada 2018 12 minut temu, perlon napisał: A coś free bez watermark'a ScreenToGif Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
perlon Opublikowano 13 Listopada 2018 Zgłoś Udostępnij Opublikowano 13 Listopada 2018 (edytowane) Godzinę temu, Parikon napisał: Dlaczego mamy przejść na System.Windows.Forms zamiast System.Windows? Tego nawet nie sugerowałem. To był tylko mój przykład na podział strukturalny. UI może być w czymkolwiek. WPF jak najbardziej. Zresztą można to robić równolegle z innymi systemami UI bez szkody dla projektu. Ważne tylko, aby aplikacja była tak napisana, żeby dało się interfejs użytkownika np podmienić na inny bez utraty funkcjonalności samej aplikacji. To oczywiście śpiew przyszłości ale warto pomyśleć o modularności i zastosowaniu wzorca MVC. Dla aplikacji typu desktop to byłby wzorzec MVP (ModelViewPresenter). Część View w tym wzorcu to może być np. Windows Presentation Foundation. Edytowane 13 Listopada 2018 przez perlon Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Parikon Opublikowano 13 Listopada 2018 Autor Zgłoś Udostępnij Opublikowano 13 Listopada 2018 (edytowane) To mały projekt, który mam nadzieję doprowadzimy do końca. Piszę doprowadzimy bo zależy mi na tym abym to nie ja podawał gotowe rozwiązania, tylko abyśmy szukali tych rozwiązań w sieci i dodawali kod do tego projektu. Oczywiście posiadam swoje rozwiązania, które już przetestowałem ale z doświadczenia wiem, że rozwiązań jest wiele a niektóre tak banalne, że co dwie głowy to nie jedna. Dodałem zdarzenie ładowania kontrolki użytkownika Usercontorl1 i teraz potrzebujemy metody, która utworzy bazę danych w katalogu gdzie znajduje się nasza dll-ka. Jak w C# sprawdzić ścieżkę do katalogu gdzie znajduje się dll-ka.? Architektura naszej aplikacji jest prosta jak budowa cepa. Dll-ka z programem znajduje się w jakimś katalogu na dysku. Nasz program musi na początku się zlokalizować. Edytowane 13 Listopada 2018 przez Parikon Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
s1016 Opublikowano 13 Listopada 2018 Zgłoś Udostępnij Opublikowano 13 Listopada 2018 string path = Path.Combine(Path.GetDirectoryName(this.GetType().Assembly.Location)); Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Parikon Opublikowano 13 Listopada 2018 Autor Zgłoś Udostępnij Opublikowano 13 Listopada 2018 wstawiłem ten kod do private void UserControl_Loaded(object sender, RoutedEventArgs e) { string path = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(this.GetType().Assembly.Location)); MessageBox.Show(path); } przy czym ponieważ Visual studio podkreślało element Path twierdząc, że nie zna jego rozwinięcia w ten sposób podanego dobrze będzie dodać System.IO przed słowem Path, gdyż Path jest także w innej bibliotece. Teraz gdy skompilujemy naszą dll i uruchomimy w Zwcad otrzymamy komunikat z podaną ścieżką do naszej dll-ki. Scieżka pozwoli na utworzyć bazę właśnie w katalogu z naszym programem a nie w katalogu wpisanym na sztywno do programu. Architektura naszego programu zakłada, że baza będzie tworzono w katalogu zawierającym nasz program. Po sprawdzeniu, że nasz kod zwraca nam string o jaki nam chodzi możemy wyłączyć wyświetlanie wiadomości private void UserControl_Loaded(object sender, RoutedEventArgs e) { string path = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(this.GetType().Assembly.Location)); //MessageBox.Show(path); } Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
perlon Opublikowano 13 Listopada 2018 Zgłoś Udostępnij Opublikowano 13 Listopada 2018 (edytowane) A może by wyłączyć właściwość path do innej klasy np. klasy służącej do zapisu i odczytu konfiguracji całej aplikacji i pobierać tą wartość w dowolnym miejscu i czasie. Ponadto można uzupełnić konfigurację o możliwość wskazania innej lokalizacji pliku bazy danych jeżeli tylko zajdzie taka potrzeba. Można to ustawiać np. w osobnym formularzu Ustawienia. Poniżej klasa typu Singleton która może trzymać różne ustawienia aplikacji. Umieściłem w niej funkcje zapisujące i odczytujące parametry dowolnego okna dialogowego w rejestrze windows. Dla każdego wywołanego okna można w zdarzeniu Loaded umieścić wywołanie var config = new ProjektForumSettings(); config.RestoreSettingsWindow(config.AppName, this); w zdarzeniu Closed var config = new ProjektForumSettings(); config.SaveSettingsWindow(config.AppName, this); Klasa ta przyjmuje domyślne pewne wartości (appName i appPath) ale można te wartości ustawiać np. przy starcie aplikacji. Będą one dostępne w całej aplikacji zawsze takie same. Zakłada również że ona sama znajduje się w tym samym assmebly co reszta aplikacji dla prawidłowego ustawienia appName i appPath. A klasa może wyglądać np. tak: using Microsoft.Win32; using System.Windows; namespace ProjektForum { public sealed class ProjektForumSettings { private static ProjektForumSettings instance = null; private int counter = 0; private string appPath; private string appName; public static ProjektForumSettings Instance { get { if (instance == null) { instance = new ProjektForumSettings(); } return instance; } } private ProjektForumSettings() { counter = 1; appPath = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(this.GetType().Assembly.Location)); appName = this.GetType().Assembly.GetName().Name; } public string AppPath { get { return appPath; } } public string AppName { get { return appName; } } public void SetAppPath(string path) { appPath = path; } public void SetAppName(string name) { appName = name; } public void SetKeyToRegister(string appName, string name, object value) { RegistryKey regKey = Registry.CurrentUser.OpenSubKey("Software", true); RegistryKey subKey = regKey.CreateSubKey(appName); subKey.SetValue(name, value); } public object GetKeyFromRegister(string appName, string name, object defaultValue) { RegistryKey regKey = Registry.CurrentUser.OpenSubKey("Software", true); RegistryKey subKey = regKey.CreateSubKey(appName); return subKey.GetValue(name, defaultValue); } public void SaveSettingsWindow(string appName, Window appWindow) { SetKeyToRegister(appName, "WindowState", (int)appWindow.WindowState); SetKeyToRegister(appName, "WindowLeft", appWindow.RestoreBounds.Left); SetKeyToRegister(appName, "WindowTop", appWindow.RestoreBounds.Top); SetKeyToRegister(appName, "WindowWidth", appWindow.RestoreBounds.Width); SetKeyToRegister(appName, "WindowHeight", appWindow.RestoreBounds.Height); } public void RestoreSettingsWindow(string appName, Window appWindow) { appWindow.Left = (int)GetKeyFromRegister(appName, "FormLeft", appWindow.Left); appWindow.Top = (int)GetKeyFromRegister(appName, "FormTop", appWindow.Top); appWindow.Width = (int)GetKeyFromRegister(appName, "FormWidth", appWindow.Width); appWindow.Height = (int)GetKeyFromRegister(appName, "FormHeight", appWindow.Height); appWindow.WindowState = (WindowState)GetKeyFromRegister(appName, "WindowState", appWindow.WindowState); } } } Edytowane 13 Listopada 2018 przez perlon Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
perlon Opublikowano 13 Listopada 2018 Zgłoś Udostępnij Opublikowano 13 Listopada 2018 Można nieco przyspieszyć pracę z kodem i umożliwić debugowanie kodu. Szczegóły na filmiku. Z góry przepraszam za słaby warsztat w nagrywaniu bo to mój pierwszy raz 😉 Parikon, s1016, dmatusz3 i 1 inny 1 3 Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
perlon Opublikowano 14 Listopada 2018 Zgłoś Udostępnij Opublikowano 14 Listopada 2018 Jeszcze tylko drobna uwaga. W powyższym polecenie (netload "appdebug") można wołać bezpośrednio z pliku netload.scr. Używanie pośrednika autostart.lsp w zasadzie jest nadmiarowe, ale jakoś tak z przyzwyczajenia kod lispowy trzymam w .lsp. Przy innych okazjach w tym pliku wykonuję inne operacje dla meritum sprawy w naszym przypadku nieistotne. Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Parikon Opublikowano 15 Listopada 2018 Autor Zgłoś Udostępnij Opublikowano 15 Listopada 2018 Nie wiem co się stało, ale po ostatnim poście nie mogę połączyć się z https://forum.cad.info.pl pakiety wysyłane przez moją przeglądarkę grzęzną na serwerach SPRINT-DC-HAWE.net.hawetelecom.pl (77.242.225.94). Zainstalowałem więc operę i po włączeniu vpn problem zniknął. Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Parikon Opublikowano 15 Listopada 2018 Autor Zgłoś Udostępnij Opublikowano 15 Listopada 2018 Chciałem w tym temacie pokazać jak tworzę własną bazę danych projektów jako aplikację opartą o system bazodanowy SQLite. Wszelkie uwagi są mile widziane oraz pomocne, ale nie chciałbym za bardzo zagłębiać się w szczegóły techniczne, których mogę jako amator nie do końca rozumieć, a bardziej rozwiązywać konkretne zadania. Zróbmy więc może najpierw aplikację WPF, która będzie tworzyć i obsługiwać naszą bazę, a potem, to co uznamy za słuszne przerzucimy do biblioteki klas, którą będzie można używać w rożnych programach. Sprawdzanie działania kodu będzie szybsze. Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Parikon Opublikowano 15 Listopada 2018 Autor Zgłoś Udostępnij Opublikowano 15 Listopada 2018 (edytowane) Do tej pory w celu poznania ścieżki do programu używałem takiego kodu using System.Reflection; string path = (new System.Uri(Assembly.GetExecutingAssembly().CodeBase)).AbsolutePath; //wyodrębniamy ściężkę do katalogu i jesli nie istnieje tworzymy naszą bazę string sciezka = System.IO.Path.GetDirectoryName(path) + "\\Bazy" + "\\projekty.sql"; SQLiteConnection db_con = new SQLiteConnection("Data Source = " + sciezka + "; Version = 3"); //utworzenie kolumn db_con.Open(); SQLiteCommand db_cmd = db_con.CreateCommand(); db_cmd.CommandType = CommandType.Text; db_cmd.CommandText = "CREATE TABLE IF NOT EXISTS projekt_lista ([Numer projektu] INT, [Nazwa projektu] TEXT, [Data dodania] TEXT, [Data zakończenia] TEXT, [Ścieżka] TEXT)"; db_cmd.ExecuteNonQuery(); db_con.Close(); Edytowane 15 Listopada 2018 przez Parikon Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
perlon Opublikowano 16 Listopada 2018 Zgłoś Udostępnij Opublikowano 16 Listopada 2018 (edytowane) Drobna uwaga. Plik z bazą danych nie powinien mieć rozszerzenia .sql Zwyczajowo .sql jest to typ pliku zawierającego skrypt w języku SQL. Warto zmienić nazwę bazy na np. "projekty.sqlite". DB Browser for SQLite który sobie zainstalowałem przy otwieraniu bazy poszukuje plików z jednym z rozszerzeń : .db .sqlite .sqlite3 lub db3 I jeszcze jedna uwaga. Wydaje mi się że w nazwach kolumn należałoby unikać spacji. Może to w przyszłości powodować komplikacje przy migracjach na inne platformy lub wprowadzać niejednoznaczności przy np. parsowaniu nazw kolumn jeżeli kiedyś taka potrzeba zajdzie. Wpisanie podkreślenia zamiast spacji nic nie kosztuje a daje jednoznaczność nazwy. Edytowane 16 Listopada 2018 przez perlon Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Parikon Opublikowano 16 Listopada 2018 Autor Zgłoś Udostępnij Opublikowano 16 Listopada 2018 Dzięki za uwagę. Wczoraj właśnie zmieniłem rozszerzenie bazy *.wgb jak to miałem w PI na sql po właczeniu DB Brows... Ale teraz zauważyłem, ze wyświetliło się skrótowo sql, gdyż kontrolka była za krótka, a po jej rozwinięciu jest sqlite. Także ustawmy na sqlite, gdyż w innym przypadku przy otwieraniu bazy tym programem trzeba wyłączyć ustawiony tam filtr wyboru. Co do nazw kolumn to pozostawmy jak jest, będzie bardziej "profesjonalnie" o ile nasz projekt będzie można nazwać profesjonalnym. string path = (new System.Uri(Assembly.GetExecutingAssembly().CodeBase)).AbsolutePath; //wyodrębniamy ściężkę do katalogu i jesli nie istnieje tworzymy naszą bazę string sciezka = System.IO.Path.GetDirectoryName(path) + "\\Bazy" + "\\projekty.sqlite"; SQLiteConnection db_con = new SQLiteConnection("Data Source = " + sciezka + "; Version = 3"); //utworzenie kolumn db_con.Open(); SQLiteCommand db_cmd = db_con.CreateCommand(); db_cmd.CommandType = CommandType.Text; db_cmd.CommandText = "CREATE TABLE IF NOT EXISTS projekt_lista ([Numer projektu] INT, [Nazwa projektu] TEXT, [Data dodania] TEXT, [Data zakończenia] TEXT, [Ścieżka] TEXT)"; db_cmd.ExecuteNonQuery(); db_con.Close(); Teraz mam pomysł, aby cały ten kod zamknąć w jednej metodzie, przy czym nazwę bazy ustalajmy w metodzie: czyli Metoda (string nazwabazy) {}. Ktoś to ogarnie? Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
perlon Opublikowano 16 Listopada 2018 Zgłoś Udostępnij Opublikowano 16 Listopada 2018 Chciałbym zaproponować pewne rozwiązanie w zakresie tworzenia bazy danych rozdzielające odpowiedzialność klas. Mianowicie tworzeniem i operacjami na bazie danych nie powinna sie zajmować klasa służąca do tworzenia interfejsu użytkownika. Wkładanie logiki obsługi bazy do UI spowoduje mocne rozbudowanie metod obsługi zdarzeń i w rezultacie uzyskamy jedną superklasę wszystko robiącą w swoich metodach. Poniższa propozycja jest jedynie namiastką koncepcji, ale jeżeli zostanie zaakceptowania przez Parikona i umieszczona w kodzie, mogę tą część BackEnd'u dalej uszczegółowić. Oczywiście wszelkie uwagi, propozycje i krytyka mile widziane. Komentarze do fragmentów kodu poniżej każdej sekcji. Na początek proponuję wydzielenie struktury tabel do osobnej klasy: using System.Collections.Generic; namespace ProjektForum { public class DatabaseStructure { public static Dictionary<string,string> GetProjectsTableStructure() { Dictionary<string, string> result = new Dictionary<string, string>(); result.Add("Numer_projektu", "INT"); result.Add("Nazwa_projektu", "TEXT"); result.Add("Data_dodania", "TEXT"); result.Add("Data_zakonczenia", "TEXT"); result.Add("Sciezka", "TEXT"); return result; } } } Mamy tu metodę statyczna zwracającą słownik gdzie kluczem jest nazwa kolumny a wartością jest typ tej kolumny. Rozwiązanie nieidealne ale na początek wystarczy. Potrzebna nam będzie klasa która sprawdzi czy baza istnieje i czy zawiera odpowiednie kolumny. Umówmy się że na tym etapie używamy na sztywno bazy SQLite jak było w zamyśle Parikona aby maksymalnie uprościć obsługę. Nie budujemy przecież systemu uniwersalnego i super elastycznego. Klasa coś na taki kształt : using System; using System.Collections.Generic; using System.Data; using System.Data.SQLite; namespace ProjektForum { public class DatabaseChecker : IDisposable { private bool isDisposed = false; private SQLiteConnection connection; public DatabaseChecker(string _dbName) { connection = new SQLiteConnection($"Data Source = {_dbName}; Version = 3"); } // poniewaz w SQLite brakuje ALTER TABLE ALTER COLUMN poki co metoda jest w stanie jedynie dodac brakujaca kolumne // w przyszlosci nalezy uzupelnic funkcjonalnosc o modyfikacje typu kolumn porzez tymczasowa kopie tabeli // stworzenie nowej i skopiowanie danych ze starej na nowa tabele public void CheckTable( string tableName, Dictionary<string, string> structure) { string parseText; SQLiteCommand command; command = connection.CreateCommand(); command.CommandType = CommandType.Text; connection.Open(); // zagwarantowanie, ze tabela w bazie istnieje parseText = ParseCreateTableCommand(structure); command.CommandText = $"CREATE TABLE IF NOT EXISTS {tableName} ({parseText})"; command.ExecuteNonQuery(); // zagwarantowanie, ze tabela zawiera co najmniej kolumny z listy "structure" command.CommandText = $"PRAGMA table_info({tableName})"; var queryResult = command.ExecuteReader(); while (queryResult.Read()) { if ( !structure.ContainsKey( queryResult.GetString(1) )) { // dodaj kolumne jezeli brakuje command.CommandText = $"ALTER TABLE {tableName} ADD COLUMN {queryResult.GetString(1)} {structure[queryResult.GetString(1)]}"; command.ExecuteNonQuery(); } } connection.Close(); } string ParseCreateTableCommand(Dictionary<string, string> structure) { string result = ""; foreach( var item in structure) { if (result != "") { result += ","; } result += $"[{item.Key}] {item.Value}"; } return result; } public void Dispose() { connection.Dispose(); GC.SuppressFinalize(this); } } } Przyjmuje ona w konstruktorze ścieżkę do bazy danych SQLite. Zawiera jedną publiczną metodę CheckTable której argumentami jest nazwa tabeli oraz słownik z oczekiwaną strukturą tabeli. Metoda prywatna ParseCreateTableCommand zamienia słownik na stringa w formacie wymaganym przez komendę CREATE TABLE. Klasa posiada implementację interfejsu IDisposable do zarządzania swoim istnieniem o czym już za chwilę. Jak tego użyć? Ano prosto. Klasa ma w zasadzie jeden cel. Sprawić aby w podanej bazie danych istniała tabela o podanej nazwie i określonej strukturze. Robimy to tak: using (var dc = new DatabaseChecker(databasePath + "\\" + databaseFileName)) { dc.CheckTable("projekt_lista", DatabaseStructure.GetProjectsTableStructure()); } Wywołanie klasy zamykamy w bloku using żeby nie pozostawiać śmieci w pamięci. Po to był nam potrzebny interfejs IDisposable. Metoda Dispose w klasie DatabaseChecker załatwia zniszczenie obiektu klasy SQLiteConnection. Powyższy kod należy umieścić w metodzie startowej całej aplikacji. Wg mnie zanim jeszcze zaczniemy myśleć o wyświetleniu jakiegokolwiek okna. Dobrym kandydatem na to byłaby metoda Initialize() w klasie implementującej interfejs IExtensionApplication o której była już w tym wątku i nie tylko mowa. Załatwia nam to jako taką integralność bazy danych z aplikacją i kontrolę w momencie ładowania modułu .dll poleceniem NETLOAD. Jeżeli wystąpi potrzeba dołożenia jakiegoś pola do tabeli wystarczy zmienić metodę zwracającą odpowiedni słownik, albo dołożenie nowej tabeli wymaga dołożenia kolejnej metody w klasie DatabaseStructure. Mamy jedno miejsce gdzie siedzi definicja bazy a nie gdzieś rozsiane po kodzie i trudne do znalezienia. Na koniec. Powyższe rozwiązanie jest dalece niedoskonałe i nie załatwia wielu problemów na które natrafimy w bardziej zaawansowanym korzystaniu z bazy SQL. Ponadto można je zrobić bardziej elastycznym wykorzystując wzorce projektowe w tym np. metodę wytwórczą fabryki czy uzyskiwanie instancji obiektów za pomocą wstrzykiwania zależności, ale to jest już zupełnie inna historia, a ja nie chciałbym się wymądrzać bo za mało o tym jeszcze wiem. Niemniej mam chęć się tego poduczyć i mogę pewne rozwiązania wdrożyć jeżeli tylko Parikon zechce je wprowadzić do kodu, który tu tworzymy. Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Parikon Opublikowano 16 Listopada 2018 Autor Zgłoś Udostępnij Opublikowano 16 Listopada 2018 (edytowane) Chodziło mi o coś prostszego. Poza tym, nie widzę sensu wrzucać do oddzielnej biblioteki klas class klasy lub metody, która nam będzie tworzyła taką a nie inną tabelę. Musi to być coś bardziej uniwersalnego. Krótkie wyjaśnienie do filmu, który dodam poniżej. Robimy to co pokazałem, ale uwaga, nie debugujemy programu, tylko go na koniec ponownie kompilujemy. Gdybyśmy go debugowali to program w trybie debugowania, nie będzie mógł utworzyć tabeli w ścieżce, którą sam sobie określi jako jego położenie. Widocznie nie ma uprawnień do tego katalogu. Trzeba zatem skopiować całą zawartość katalogu debug do innego katalogu oraz dodać tam folder Bazy, gdyż nasza metoda nie umie tworzyć tego folderu jak go nie ma. Edytowane 16 Listopada 2018 przez Parikon Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
perlon Opublikowano 16 Listopada 2018 Zgłoś Udostępnij Opublikowano 16 Listopada 2018 Powyższe jest proste, natomiast obciąża klasę MainWindow odpowiedzialnością za bazę danych, jej lokalizację, za ilość tabel (może być ich przecież więcej niż jedna) jak i konkretną strukturę tabel. Jest więc mniej uniwersalne niż bardziej. Ponadto klasa MainWindow nie powinna się zajmować tworzeniem bazy danych. Jej odpowiedzialnością jest komunikacja z użytkownikiem (wyświetlanie danych, przyjmowanie zdarzeń i przekazywanie ich wgłąb aplikacji). Kontroler istnienia bazy nie musi być w innej bibliotece. Znajduje się w tej samej przestrzeni nazw co klasa MainWindow czyli w tym samym dll ma tylko inną odpowiedzialność. Jeżeli zajdzie potrzeba w aplikacji dołożenia dodatkowej kolumny do tabeli "projekt_lista" np. "typ_projektu" (projekt budowalny, projekt wykonawczy, koncepcja...etc) to co będzie konieczne żeby takie pole dodać i jeszcze w dodatku bez utraty dotychczasowych danych? Czy na pewno powinna się tym zająć klasa MainWindow. Nie żebym się czepiał. Proszę potraktuj to jako głos w dyskusji. Odsyłam też do https://pl.wikipedia.org/wiki/Antywzorzec_projektowy (szukaj : Boski obiekt ) i https://pl.wikipedia.org/wiki/Spaghetti_code Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
perlon Opublikowano 16 Listopada 2018 Zgłoś Udostępnij Opublikowano 16 Listopada 2018 Dla zainteresowanych. W metodzie CheckTable() zamiast : command.CommandText = $"PRAGMA table_info({tableName})"; var queryResult = command.ExecuteReader(); while (queryResult.Read()) { if ( !structure.ContainsKey( queryResult.GetString(1) )) { // dodaj kolumne jezeli brakuje command.CommandText = $"ALTER TABLE {tableName} ADD COLUMN {queryResult.GetString(1)} {structure[queryResult.GetString(1)]}"; command.ExecuteNonQuery(); } } connection.Close(); powinno być : command.CommandText = $"PRAGMA table_info({tableName})"; var queryResult = command.ExecuteReader(); while (queryResult.Read()) { structure.Remove(queryResult.GetString(1)); } queryResult.Close(); foreach( var item in structure) { // dodaj kolumne jezeli brakuje command.CommandText = $"ALTER TABLE {tableName} ADD COLUMN {item.Key} {item.Value}"; command.ExecuteNonQuery(); } connection.Close(); command.Dispose(); Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Parikon Opublikowano 16 Listopada 2018 Autor Zgłoś Udostępnij Opublikowano 16 Listopada 2018 Wyobrażam sobie, że pierwsza wersja programu ma te kolumny, które będzie tworzyć. Więc nie widzę powodu aby program dodawał kolumny i sprawdzał ile kolumn ma baza i jakie. Co najwyżej możemy dodać tabele o nazwie wersja i w niej jedną kolumnę o nazwie wersja i jeden wiersz z liczbą 1. W przypadku gdybyśmy wypełnili taką prostą bazę, a potem zrobili wersję programu, w której program się rozwinął do większej ilości kolumn i tabel można by robić konwertery danych z danej wersji bazy do wersji jaka tworzy aktualnie program. Natomiast teraz spróbowałem udolnie lub nie wprowadzić inną klasę niż klasę typu Window w której działają wszystkie zdarzenia jakie automatycznie tworzy mi Visul Studio. Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
perlon Opublikowano 17 Listopada 2018 Zgłoś Udostępnij Opublikowano 17 Listopada 2018 (edytowane) Wersjonowanie bazy jest dobrym pomysłem. Warto w momencie jej tworzenia zapisać w niej informację, którą wersją programu została stworzona. Dobrymi kandydatami do zapisu są poniżej zaznaczone pola. Jedno z nich, być może wszystkie. Dostępność tych danych z poziomu kodu: var ass = Assembly.GetExecutingAssembly(); var attr = ass.GetCustomAttributes(false); var assemblyVersion = ass.GetName().Version.ToString(); var guid = ((GuidAttribute)attr.Single(a => a.GetType().Name == "GuidAttribute")).Value; var fileVersion1 = ((AssemblyFileVersionAttribute)attr.Single(a => a.GetType().Name == "AssemblyFileVersionAttribute")).Version; var fileVersion2 = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion; Być może są jeszcze inne na to sposoby, które chętnie poznam. Edytowane 17 Listopada 2018 przez perlon Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Parikon Opublikowano 17 Listopada 2018 Autor Zgłoś Udostępnij Opublikowano 17 Listopada 2018 Trochę inaczej podchodzę do zagadnienia. Nie interesuje mnie wersja programu, gdyż wiele wersji programu może tworzyć tę samą strukturę bazy, czyli inaczej, tę samą wersję bazy. Dlatego jak napisałem wyżej, nasz program oprócz tabeli projekt_lista będzie tworzył tabelę wersja_bazy. Dodatkowo zmieniam naszą własną metodę o nazwie Addbaseifnotexit() w taki sposób, że w przypadku gdy tworzenie się nie powiedzie i program uzna, że nie ma bazy o podanej nazwie lub brak interesujących tabel to metoda zwróci wartość false (fałsz) lub true (prawda). using System; using System.Collections.Generic; using System.Data; using System.Data.SQLite; using System.Linq; using System.Reflection; using System.Text; using System.Windows; namespace projekty { class Base { /// <summary> /// Tworzy bazę jeśli nie istnieje i tabelę projekt_lista, jesli coś się nie powiodło zwraca false /// </summary> public static bool AddBaseifnotexists() { bool ok; try { string path = (new System.Uri(Assembly.GetExecutingAssembly().CodeBase)).AbsolutePath; //wyodrębniamy ściężkę do katalogu i jesli nie istnieje tworzymy naszą bazę string sciezka = System.IO.Path.GetDirectoryName(path) + "\\Bazy" + "\\projekty.sqlite"; SQLiteConnection db_con = new SQLiteConnection("Data Source = " + sciezka + "; Version = 3"); //utworzenie tabeli db_con.Open(); SQLiteCommand db_cmd = db_con.CreateCommand(); db_cmd.CommandType = CommandType.Text; db_cmd.CommandText = "CREATE TABLE IF NOT EXISTS projekt_lista ([Numer projektu] INT, [Nazwa projektu] TEXT, [Data dodania] TEXT, [Data zakończenia] TEXT, [Ścieżka] TEXT)"; db_cmd.ExecuteNonQuery(); db_cmd.CommandText = "CREATE TABLE IF NOT EXISTS wersja_bazy ([Numer wersji] INT)"; db_cmd.ExecuteNonQuery(); db_con.Close(); ok = true; } catch { MessageBox.Show("Nie utworzono bazy. Prawdopodobnie brak możliwości zapisu w katalogu z programem lub brak katalogu Bazy. Program zostanie zamknięty", "Projekty-info"); ok = false; } return ok; } } } Gdy ten kod się nie powiedzie wyświetli się komunikat i program się nie otworzy. 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.Navigation; using System.Windows.Shapes; namespace projekty { /// <summary> /// Logika interakcji dla klasy MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Window_Loaded(object sender, RoutedEventArgs e) { bool results = Base.AddBaseifnotexists(); if (results != true) { Window.GetWindow(this).Close(); } } } } Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
perlon Opublikowano 17 Listopada 2018 Zgłoś Udostępnij Opublikowano 17 Listopada 2018 Brakuje jeszcze dopisania rekordu do tabeli "wersja_bazy", bo skoro właśnie została stworzona to na dzień dobry powinien w niej się już znaleźć jeden rekord z numerem wersji bazy. Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Parikon Opublikowano 17 Listopada 2018 Autor Zgłoś Udostępnij Opublikowano 17 Listopada 2018 (edytowane) To chyba już przyda się nowa metoda w klasie Base. Base.CheckBaseVersion(). Sprawdza czy w tabeli wersja_bazy znajduje się rekord. Jeśli nie, to go tworzy w wpisując wartość 1 do kolumny 0, gdyż nasz program tworzy taką wersję bazy. Jeśli rekord się znajduje, to metoda odczytuje wersję bazy jaka tam jest wpisana i ostatecznie zwraca numer wersji. Edytowane 17 Listopada 2018 przez Parikon Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
perlon Opublikowano 18 Listopada 2018 Zgłoś Udostępnij Opublikowano 18 Listopada 2018 Wg mnie może być OK. Proponuję wprowadzić stosowny interfejs zmieniając nieco nazwę metody skoro ma coś zwracać: public interface IBase { bool AddBaseIfNotExist(); int GetBaseVersion(); } public class Base : IBase { ... } W klasie Base wprowadzić i zaimplementować ten interface implementując metody instancyjne a nie statyczne. Taka abstrakcja pozwoli w przyszłości podmienić klasę Base() jakąś inną klasą realizującą takie same zadania na innym silniku bazy danych bez zmiany kodu klasy MainWindow. Jeżeli klasa MainWindow będzie operować na interfejsie a nie klasie konkretnej nie trzeba będzie jej zmieniać ale będzie ją można rozszerzać o nową funkcjonalność. https://pl.wikipedia.org/wiki/Zasada_otwarte-zamknięte Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Rekomendowane odpowiedzi
Dołącz do dyskusji
Możesz dodać zawartość już teraz a zarejestrować się później. Jeśli posiadasz już konto, zaloguj się aby dodać zawartość za jego pomocą.