Rekomendowane odpowiedzi

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 przez perlon

Udostępnij tego posta


Odnośnik do posta
Udostępnij na innych stronach

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 przez Parikon

Udostępnij tego posta


Odnośnik do posta
Udostępnij na innych stronach

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);
        }

 

 

 

Udostępnij tego posta


Odnośnik do posta
Udostępnij na innych stronach

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 przez perlon

Udostępnij tego posta


Odnośnik do posta
Udostępnij na innych stronach

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.

Udostępnij tego posta


Odnośnik do posta
Udostępnij na innych stronach

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ął. 

Udostępnij tego posta


Odnośnik do posta
Udostępnij na innych stronach

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.

 

Udostępnij tego posta


Odnośnik do posta
Udostępnij na innych stronach

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 przez Parikon

Udostępnij tego posta


Odnośnik do posta
Udostępnij na innych stronach

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 przez perlon

Udostępnij tego posta


Odnośnik do posta
Udostępnij na innych stronach

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? 

 

 

 

Udostępnij tego posta


Odnośnik do posta
Udostępnij na innych stronach

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.

 

Udostępnij tego posta


Odnośnik do posta
Udostępnij na innych stronach

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 przez Parikon

Udostępnij tego posta


Odnośnik do posta
Udostępnij na innych stronach

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

Udostępnij tego posta


Odnośnik do posta
Udostępnij na innych stronach

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();

 

Udostępnij tego posta


Odnośnik do posta
Udostępnij na innych stronach

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.

 

Udostępnij tego posta


Odnośnik do posta
Udostępnij na innych stronach

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.

1135306485_2018-11-1706_00_11-AppDebug-MicrosoftVisualStudio.thumb.png.531a14754faf97b1388397d43985afa6.png

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 przez perlon

Udostępnij tego posta


Odnośnik do posta
Udostępnij na innych stronach

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();
            }
        }
        
    }
}

 

Udostępnij tego posta


Odnośnik do posta
Udostępnij na innych stronach

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.

Udostępnij tego posta


Odnośnik do posta
Udostępnij na innych stronach

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 przez Parikon

Udostępnij tego posta


Odnośnik do posta
Udostępnij na innych stronach

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

 

 

 

Udostępnij tego posta


Odnośnik do posta
Udostępnij na innych stronach

Jeśli chcesz dodać odpowiedź, zaloguj się lub zarejestruj nowe konto

Jedynie zarejestrowani użytkownicy mogą komentować zawartość tej strony.

Zarejestruj nowe konto

Załóż nowe konto. To bardzo proste!

Zarejestruj się

Zaloguj się

Posiadasz już konto? Zaloguj się poniżej.

Zaloguj się