-
Postów
434 -
Dołączył
-
Ostatnia wizyta
-
Wygrane w rankingu
36
Treść opublikowana przez perlon
-
Jakoś mi się udało zrobić forka library1_zw na swoim githubie. Jest tam stworzona gałąź wersji z moim kodem. https://github.com/Perlon001/library1_zw/tree/ForkPerlon/library1_zw Myślałem że projekt byłby realizowany wspólnymi siłami, na zasadzie wspólnego zdobywania tzw. skill'a i trochę dla fanu, dając możliwość zwiększania funkcjonalności przy wspólnej pracy nad kodem. Można robić osobne nie powiązane moduły do zadań konkretnych. Każdy sobie i tylko udostępniać na licencji MIT w warunkach "takie jakie jest" i jak chcesz to sobie je weź. Tylko że jak wymyślę nową super zarypistą kotę do oznaczania rzędnych na rzutach to najłatwiej byłoby ją umieścić w moule od kot. Ale żeby ją mieć to muszę sobie nową dll'kę zmajstrować. Ja tak bardzo nie jestem przywiązany do swojego kodu żebym miał się bronić przed jego poprawianiem. Tym bardziej, że git pozwala na tworzenie wielu odgałęzień wersji i ich scalaniu wedle uznania. Muszę tylko się tylko nauczyć Wracając do sedna. Korzystając z zaproszenia do współpracy postanowiłem zmodyfikować nieco podejście do tematu rysowania kot. Na forum kilkukrotnie (pozdrawiam MartinS) była dyskusja na temat stosowania norm również w oznaczeniach na rysunków. Dlatego wymyśliłem sobie, że użytkownik bezie miał możliwość wyboru systemu rysowania (wyboru dostępnej normy) i za pomocą tej samej komendy rysować różne koty. Ta filozofia zapewne może mieć zastosowanie w wielu innych aspektach. Koty to tylko przykład jak za pomocą kiedyś wymyślonych przez mądrych ludzi wzorców realizować tego typu zadania. W tym konkretnym przypadku do tworzenia koty w odpowiednim standardzie wykorzystałem wzorzec fabryka. Duża część kodu do tworzenia koty nawet nie wie jaką kotę tworzy. Klasa abstrakcyjna BaseElevationMark dostarcza implementację wspólną dla wszystkich kot wysokościowych. Klasy konkretne w kilku linijkach załatwiają tylko te rzeczy którymi koty się różnią. Na to wszystko nałożona jest fabryka która zwraca na żądanie klasę konkretną ukrytą za interfejsem. Zdaję sobie sprawę, że mój kod ani nie jest odkrywczy ani nie jest super och i ach. Dlatego wszelkie uwagi są na prawdę mile widziane. Zapewne w kodzie są błędy, których ja jako autor nie jestem w stanie dostrzec. Jest również wiele rzeczy do poprawienia i uzupełnienia których mam świadomość. Feedbek bardzo mile widziany a za zrypkę też się nie obrażę. Mam również świadomość, że ilość linijek może nie być mniejsza niż w kodzie podstawowej biblioteki library1_zw (nie sprawdzałem). Szukałem sposobu na możliwość szybkiej rozbudowy o nowe standardy, a kod żeby spełniał zasady programowania obiektowego. Kończąc ten przydługawy elaborat chciałbym dodać, że spodziewałem się współpracy na poziomie tworzenia/pisania kodu w celach edukacyjno/hobbystycznych z efektem praktycznym, ale wygląda na to że zamysł Parikona jest nieco inny.
-
Popełniłem trochę kodu, głównie jako refaktoryzacja i uzyskanie podobnego efektu końcowego innymi metodami. Skupiłem się na części dot. kot wysokościowych. Chciałbym to wrzucić na githuba jako boczny branch ale coś mam chyba nie tak poustawiane, czegoś nie umiem albo repo jest zamknięte na obce commity . Jeszcze nie comittowałem do obcego repozytorium. Wklejanie całego kodu tutaj mija się z celem. Jak jakoś udostępnię cały kod to pozwolę sobie na kilka zdań objaśnienia co do mojego spojrzenia na to konkretne zagadnienie. Wszystko co udostępnię w ramach tego projektu będzie oczywiście na licencji MIT. Co do "zapętlenia" to rozwiązaniem chyba wystarczającym jest zamknięcie wywołania komendy w pętli nieskończonej + Exception. Klasę z komendami zrobiłem tak: [CommandMethod("KOTA_ARCH")] public void ArchElevationMark() { // co druga linijka z ponizszych jest niepotrzebna // konfiguracja z AppSettings IElevationMarkFactory markFactory1 = new ElevationMarkFactory(AppSettings.Instance.DrawingStandard).GetFactory(); // jak kto woli moze byc z łapy bez oglądania się na aktualne ustawienia IElevationMarkFactory markFactory2 = new ElevationMarkFactory(DrawingStandards.PN_B_01025).GetFactory(); // ustawienia defaultowe IElevationMark elevationMark1 = markFactory1.CreateElevationMark(ElevationMarkTypes.archMark); // albo wstrzykujemy konfiguracje. ElevationMarkConfig config = new ElevationMarkConfig(); config.ElevationMarkColor = Color.FromColorIndex(ColorMethod.ByAci, 5); config.ElevationMarkLayer = "OddzielnaWarstwaNaKoty"; IElevationMark elevationMark2 = markFactory1.CreateElevationMark(ElevationMarkTypes.archMark, config); try { while(true) { elevationMark1.Create(); } } catch (System.Exception ex) { Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(ex.Message); } } [CommandMethod("KOTA_KONSTR")] public void ConstrElevationMark() { // tutaj wersja simply IElevationMarkFactory markFactory = new ElevationMarkFactory(AppSettings.Instance.DrawingStandard).GetFactory(); IElevationMark elevationMark = markFactory.CreateElevationMark(ElevationMarkTypes.constrMark); try { while(true) { elevationMark.Create(); } } catch (System.Exception ex) { Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(ex.Message); } } Metoda w której wskazuję punkty wygląda tak: protected virtual void GetPoints() { Document acDoc = Application.DocumentManager.MdiActiveDocument; Application.SetSystemVariable("Osmode", 512); PromptPointOptions promptOptions = new PromptPointOptions("\nWskaż punkt wysokościowy: "); promptOptions.AllowNone = true; PromptPointResult pointResult = acDoc.Editor.GetPoint(promptOptions); if (pointResult.Status == PromptStatus.Cancel || pointResult.Status == PromptStatus.None) { throw new Exception("Cancel"); } elevationPoint = pointResult.Value; Application.SetSystemVariable("Osmode", 0); promptOptions.Message = "\nWskaż stronę : "; promptOptions.UseBasePoint = true; promptOptions.BasePoint = elevationPoint; pointResult = acDoc.Editor.GetPoint(promptOptions); if (pointResult.Status == PromptStatus.Cancel || pointResult.Status == PromptStatus.None) { throw new Exception("Cancel"); } side = GetSides(pointResult.Value); elevationPoint = new Point3d(Math.Round(elevationPoint.X, 3), Math.Round(elevationPoint.Y, 3), elevationPoint.Z); } Jak widać przy każdym oczekiwaniu na punkt jeżeli wciśnie się ESC lub ENTER pointResult otrzymuje status odpowiednio Cancel lub None. Wystarczy to sprawdzić i rzucić wyjątek który wywędruje aż do komendy startowej i tam dopiero zostanie obsłużony. Mój CommandMethod wygląda nieco dziwnie ale przy najbliższej okazji spróbuję wyjaśnić co się skąd wzięło. Następny ranek... Wygląda, że repozytorium https://github.com/Parikon/library1_zw jest zamknięte dla obcych commitów 😞
-
Jakiś czas temu wpadła mi w ręce taka pozycja: https://helion.pl/ksiazki/refaktoryzacja-ulepszanie-struktury-istniejacego-kodu-martin-fowler-kent-beck-john-brant-william-opdy,refukv.htm#format/d Trochę już lat od wydania ale mi osobiście zmieniła spojrzenie na sporo rzeczy jeżeli chodzi o optymalizacje kodu. W przypadku kot wysokościowych są duże fragmenty powtórzonego kodu np. tego związanego z pobieraniem od użytkownika punktów, obsługą warstw czy rysowaniem tekstu. Dla każdej z kot jest on identyczny. Może warto wydzielić go do osobnej metody tym bardziej, że jak przyjdzie pomysł na inne koty to ten kod będzie potrzebny po raz kolejny. Krótszy kod łatwiej się zarządza i ewentualne poprawki robi się w jednym miejscu a nie w wielu powtórzeniach.
-
Może w taki deseń? public class Start : zzr.IExtensionApplication { string path = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); public static zza.Document doc = zza.Application.DocumentManager.MdiActiveDocument; zze.Editor ed = doc.Editor; public void Initialize() { ed.WriteMessage("\nPI 2019 MIT Edition. Niektóre moduły PI korzystają z silnika bazy danych SQLite - www.sqlite.org."); ed.WriteMessage("\nŁadowanie modułów:"); Zaladuj("skala_zw.dll"); Zaladuj("cos_innego.dll"); Zaladuj("jeszcze_jakis_inny_modul.dll"); } public void Terminate() { } public void Zaladuj(string nazwadll) { ed.WriteMessage("\nŁadowanie " + nazwadll + new String('.',20 - nazwadll.Length)); string sciezka = path + "\\" + nazwadll; try { Assembly.LoadFrom(sciezka); ed.WriteMessage(" załadowane."); } catch (System.Exception ex) { ed.WriteMessage(" Błąd : " + ex.Message); } } }
-
Tu chyba jest mała nieścisłość bo zamiast catch (System.Exception ex) { ed.WriteMessage(ex.Message); zlicz = zlicz = 1; } powinno być catch (System.Exception ex) { ed.WriteMessage(ex.Message); zlicz = 1; } lub ewentualnie jak niżej jeżeli zlicz jest licznikiem nieudanych ładowań dll'ek catch (System.Exception ex) { ed.WriteMessage(ex.Message); zlicz += 1; } Wydaje si
-
Po ściągnięciu z GitHub'a kompiluje się prawidłowo ale uruchomienie zwraca takie okno: Zależność jest tylko jedna i jest w porządku <?xml version="1.0" encoding="utf-8"?> <packages> <package id="System.Data.SQLite.Core" version="1.0.109.2" targetFramework="net40" /> </packages> Wujek google podpowiada, że SQLite nie wie jaką bibliotekę załadować x86 czy x64. U mnie pomogło rzucenie w konsoli managera pakietów : Update-Package -reinstall
-
Zmienna systemowa ORBITAUTOTARGET [Wprowadzono w ZWCAD 2024]
perlon odpowiedział(a) na perlon pytanie w Propozycje nowych funkcji
Tak. Tylko że ja robię orbit przez Shift+SKM a nie przez _3dorbit. Przy 3dorbit jest jak trzeba ale trzeba znaleźć ikonę a Shift+SKM jest bez celowania w ikonę. -
Zmienna systemowa ORBITAUTOTARGET [Wprowadzono w ZWCAD 2024]
perlon opublikował(a) pytanie w Propozycje nowych funkcji
Czy są plany wprowadzenia tej zmiennej systemowej? Brak filtrowania obiektów przy obrocie trochę zamula sam proces obracania. -
Nie działa shift unselect face na solidach
perlon odpowiedział(a) na perlon temat w ZWCAD Standard i Professional
Tak -
Nie działa shift unselect face na solidach
perlon opublikował(a) temat w ZWCAD Standard i Professional
Operacje na solidach typu Extrude/Move etc. Po wskazaniu krawędzi są zaznaczane dwa sąsiednie face'y. Żeby odjąć jednego face'a mogę użyć opcji Remove ale w poprzednich wersjach można było odjąć przez selekcję z shiftem innej krawędzi. W 2019 shift nie działa przy "deselekcji". Nie wiem czy coś mam nie ustawione czy ten typ tak ma, a wtedy trzeba by to zgłosić. VERNUM = "2018.10.23(38586)_x64" -
W uzupełnieniu : zamiast (long)version powinienem użyć Convert.ToInt16(version) i będzie int
-
Wyszło samo, bo do odczytania wersji bazy użyłem takiego kodu : this.db_con.Open(); SQLiteCommand db_cmd = db_con.CreateCommand(); db_cmd.CommandText = "SELECT MAX([Numer wersji]) FROM wersja_bazy"; var version = db_cmd.ExecuteScalar(); this.db_con.Close(); niestety (int)version; rzuca wyjątek : "Określone rzutowanie jest nieprawidłowe." W tym konkretnym przypadku db_cmd.ExecuteScalar() zwraca object{long}, który niestety nie daje się zrzutować na int. Uzasadnienie: Swego czasu trochę kodowałem hobbystycznie w Clipper'ze między innymi na bazach MySQL. Został mi (może durny) nawyk który przeniosłem na C# że do CREATE, INSERT, DELETE używam w C# metody ExecuteNonQuery (zwraca ilość przetworzonych rekordów) a do SELECT używam ExecuteScalar lub ExecuteReader (zwraca wybrane rekordy) w zależności czy mam pobrać jeden czy więcej rekordów. Uzasadnienie może słabe, ale takie mi tylko przychodzi do głowy. Prawdę mówiąc dla mnie bardziej naturalną technologią w języku obiektowym byłby dostęp do bazy za pośrednictwem ORM'a, ale nie chciałbym tego tematu tu rozwijać. Twój kod pokazuje, że można to zrobić inaczej i myślę że nie ma o co kruszyć kopii.
-
W zasadzie AddBaseIfNotExist() może być typu void i nic nie zwracać. Jeżeli coś pójdzie nie tak będzie wyjątek jeżeli będzie wszystko ok to następny krok czyli sprawdzenie wersji bazy.
-
Można wywalić obsługę wyjątków z klasy Base() pozostawiając rzucanie wyjątków. Być może trzeba "zagęścić" rzucanie wyjątków. Być może trzeba stworzyć specjalny typ dziedziczący po Exception(). public class Base { // zmienna path nie jest potrzebna w zadnej z metod // można ją spokojnie przenieść do konstruktora jako zmienną prywatną private string path; // do połączenia wystarczy db_con ustawiony raz w konstruktorze private SQLiteConnection db_con; public Base(string _path) { // w zasadzie path nie jest potrzebny poza konstruktorem // żeby połaczyć sie ponownie z bazą w poszczególnyhc metodach wystarczy // zapamietać obiekt SQLiteConnection db_con this.path = _path + "\\Bazy"; if (!Directory.Exists(this.path)) { Directory.CreateDirectory(this.path); if (!Directory.Exists(this.path)) { throw new Exception("Błąd zakładania katalogu z bazą"); } } string sciezkaBaza = this.path + "\\projekty.sqlite"; this.db_con = new SQLiteConnection($"Data Source = {sciezkaBaza}; Version = 3"); } public bool AddBaseIfNotExist() { bool ok = true; this.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 = "SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'wersja_bazy'"; var result = db_cmd.ExecuteScalar(); if (result == DBNull.Value) { db_cmd.CommandText = "CREATE TABLE wersja_bazy ([Numer wersji] INT)"; db_cmd.ExecuteNonQuery(); db_cmd.CommandText = "INSERT INTO wersja_bazy VALUES (1)"; db_cmd.ExecuteNonQuery(); } this.db_con.Close(); return ok; } public long GetBaseVersion() { this.db_con.Open(); SQLiteCommand db_cmd = db_con.CreateCommand(); db_cmd.CommandText = "SELECT MAX([Numer wersji]) FROM wersja_bazy"; var version = db_cmd.ExecuteScalar(); this.db_con.Close(); if (version == DBNull.Value) { throw new Exception("Tabela wersja_bazy powinna zawierać co najmniej jeden rekord. Coś poszło nie tak przy zakładaniu tej tabeli"); } else { return (long)version; } } } W każdym bądź razie co by się nie stało, obsługa wyjątku może być tu i będzie tylko jedno miejsce "wyskoku" z programu. try { var baza = new Base(path); if (baza.AddBaseIfNotExist()) { if (baza.GetBaseVersion() != 1) { // tutaj dałbym konwerter na starej bazy na nową, ale narazie nie mamy takiego konwertera więc program tego nie zrobi throw new Exception("Nieprawidłowa wersja bazy. Konwersja niemożliwa w tej wersji programu.Nastąpi zamknięcie programu"); } } } catch (Exception ex) { MessageBox.Show(ex.Message, "Projekty-info"); this.Close(); }
-
Hmmm... Nie bardzo kumam. Zamiast 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; zaproponowałem catch (Exception ex) { MessageBox.Show(ex.Message, "Projekty-info"); ok = false; } return ok; Powyższe wyświetla faktyczną przyczynę skoku do bloku catch. Powrót do kodu : bool results = baza.AddBaseIfNotExist(); if (results != true) { Window.GetWindow(this).Close(); } w zasadzie kończy sprawę. Kod if (!Directory.Exists(this.path)) { Directory.CreateDirectory(this.path); if (!Directory.Exists(this.path)) { throw new Exception("Błąd zakładania katalogu z bazą"); } } string sciezkaBaza = this.path + "\\projekty.sqlite"; this.db_con = new SQLiteConnection($"Data Source = {sciezkaBaza}; Version = 3"); } rzuca wyjątek w konstruktorze bez jego obsługi. Nie jest zamknięty w żadnym try catch więc powinien wywalić aplikację zanim dojdzie do metody AddBaseIfNotExist
-
Faktycznie nie przemyślałem tego do końca. Teraz powinno być lepiej. public bool AddBaseIfNotExist() { bool ok; try { this.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 = "SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'wersja_bazy'"; var result = db_cmd.ExecuteScalar(); if(result == DBNull.Value) { db_cmd.CommandText = "CREATE TABLE wersja_bazy ([Numer wersji] INT)"; db_cmd.ExecuteNonQuery(); db_cmd.CommandText = "INSERT INTO wersja_bazy VALUES (1)"; db_cmd.ExecuteNonQuery(); } this.db_con.Close(); ok = true; } catch( Exception ex ) { MessageBox.Show(ex.Message, "Projekty-info"); ok = false; } return ok; }
-
A może taka wersja klasy Base() : public class Base { // zmienna path nie jest potrzebna w zadnej z metod // można ją spokojnie przenieść do konstruktora jako zmienną prywatną // ale może się przyda więc zostaje private string path; // do połączenia wystarczy db_con ustawiony raz w konstruktorze private SQLiteConnection db_con; public Base(string _path) { // w zasadzie path nie jest potrzebny poza konstruktorem // żeby połaczyć sie ponownie z bazą w poszczególnyhc metodach wystarczy // zapamietać obiekt SQLiteConnection db_con this.path = _path + "\\Bazy"; if (!Directory.Exists(this.path)) { Directory.CreateDirectory(this.path); if (!Directory.Exists(this.path)) { throw new Exception("Błąd zakładania katalogu z bazą"); } } string sciezkaBaza = this.path + "\\projekty.sqlite"; this.db_con = new SQLiteConnection($"Data Source = {sciezkaBaza}; Version = 3"); } public bool AddBaseIfNotExist() { bool ok; try { this.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_cmd.CommandText = "INSERT INTO wersja_bazy VALUES (1)"; db_cmd.ExecuteNonQuery(); this.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; } public long GetBaseVersion() { long result = 0; this.db_con.Open(); SQLiteCommand db_cmd = db_con.CreateCommand(); db_cmd.CommandText = "SELECT MAX([Numer wersji]) FROM wersja_bazy"; var version = db_cmd.ExecuteScalar(); this.db_con.Close(); if (version == DBNull.Value) { throw new Exception("Tabela wersja_bazy powinna zawierać co najmniej jeden rekord. Coś poszło nie tak przy zakładaniu tej tabeli"); } else { result = (long)version; } return result; } }
-
Żeby mieć zmienną path dostępną wewnątrz klasy dla metod trzeba mieć instancję klasy i przekazać wartość przez konstruktor klasy: public class Base { string path; public Base(string _path) { this.path = _path; } public bool AddBaseIfNotExist() { bool ok; try { string sciezkaBaza = this.path + "\\Bazy"; // } catch { // ... } return ok; } } Żeby zadziałało trzeba powoływać obiekt klasy Base() a metoda już bez argumentu bo klasa ma pole path. var base = new Base(path); var result = base.AddBaseIfNotExist(); Jeżeli pole statyczne w klasie Base() to można tak : public static string path = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
-
Jeszcze raz wróciłem do pierwszego posta i po chwili refleksji chciałbym Parikona przeprosić. Źle odczytałem intencję. Jako gospodarz tego wątku pokazuje swoją drogę do wyznaczonego w zamyśle efektu końcowego zaznaczając że powstaje tu kod "as is". Być może odniosłem mylne wrażenie, że moglibyśmy wspólnie jako użytkownicy forum zainteresowani programowaniem wypracować rozwiązania, które będą swego rodzaju efektem wspólnego uczenia się. Niemniej jednak z zainteresowaniem będę obserwował rozwój tego narzędzia. Miałbym również propozycję (nie wiem czy możliwą technicznie) aby w pierwszym poście umieścić link do zasobu zawierającego aktualny stan kompletnego kodu - najlepiej całej solucji. Będzie to pomocne przy indywidualnym testowaniu programu. Być może wystąpią przypadki użycia nie uwzględnione przez autora. Zgłoszenia takie zapewne będą pomocne w rozwoju nakładki.
-
Mi takie coś działa: var dbPath = "D:\\dev\\Visual Studio 2017 Community"; var dbName = "projekty.sqlite3"; Directory.CreateDirectory($"{dbPath}"); connection = new SQLiteConnection($"Data Source = {dbPath}\\{dbName}; Version = 3"); Może wstaw u siebie Directory.CreateDirectory($"{sciezkaBaza}");
-
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
-
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.
-
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.
-
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();
-
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