Zmienna "stała" przez całe działanie programu definiowana przy uruchomieniu

Zmienna "stała" przez całe działanie programu definiowana przy uruchomieniu
DZ
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 115
0

Witam

Potrzebuję pomocy z następującym problemem:

  • piszę w C# program oparty o windows forms gdzie pierwsze form to logowanie i w tym miejscu pobieram kilka informacji z bazy (id operatora, id administracji, dsn bazy danych)
  • po zalogowaniu dane podane powyżej (id operatora, id administracji, dsn bazy danych) mają być zapamiętane i możliwe do wykorzystanie przez cały czas działania programu w każdym kolejnym form
    Pytanie do Was - jak to zrealizować ??
    W tej chwili zrobiłem statyczną klasę w której zdefiniowałem zmienne:
Kopiuj
 public static class Zmienne
    {        
        public static int IDOperatora_zalogowanego;
        public static int IDAdministracji;
        public static string DSNAdministracji; 
     }

W form logowania mam zdefiniowane:

Kopiuj
Zmienne.IDOperatora_zalogowanego = this.IDOperatora;
Zmienne.IDAdministracji = this.IDAdministracji;
Zmienne.DSNAdministracji = this.DSNAdministracji;

Wszystko działa do pierwszego form - dane są przekazywane, na ich podstawie jest pobrane menu z bazy, działa sprawdzenie uprawnień itp. W momencie jak z form głównego chcę wywołać kolejny form i chcę w tym celu wykorzystać

Kopiuj
Zmienne.DSNAdministracji

żeby połączyć się z bazą i pobrać dane wtedy zmienna ta jest już pusta.

_13th_Dragon
  • Rejestracja: dni
  • Ostatnio: dni
0

Poczytać pod hasłem: singleton

DZ
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 115
0

Czytałem, zrobiłem ale bez zmian. Znając moje szczęście źle zrobiłem ale podam jak :

Kopiuj
public sealed class Zmienne
    {
        static Zmienne instance = null;
        
        public int IDOperatora_zalogowanego;
        public int IDAdministracji;
        public string DSNAdministracji; 

        Zmienne()
        {
        }

        public static Zmienne Instance
        {
            get 
            {
                if (instance==null)
                {
                    instance = new Zmienne();
                }
                return instance;
            }
        }
    }

Później odwołanie przez np

Kopiuj
Zmienne.Instance.DSNAdministracji
Azarien
  • Rejestracja: dni
  • Ostatnio: dni
0

wtedy zmienna ta jest już pusta.
Nie może być pusta. Pola statyczne nie znikają ot tak sobie — raczej twój kod wykonuje się w innej kolejności niż ci się wydaje (najpierw odczytujesz, a potem zapisujesz) albo gdzieś indziej te pola zerujesz.

DZ
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 115
0

To lecimy od początku - może na tej podstawie uda się Wam znaleźć mój błąd.

Mamy form logowania. W nim pobieramy z bazy wartości i zapisujemy do zmiennych:

Kopiuj
Zmienne.IDOperatora_zalogowanego = this.IDOperatora;
Zmienne.IDAdministracji = this.IDAdministracji;
Zmienne.DSNAdministracji = this.DSNAdministracji;

Po drodze oczywiście jest jeszcze trochę kodu ale to nas głównie interesuje. Ustawiamy bool zalogowano na true i robimy

Kopiuj
this.Close();

W samym Main mamy:

Kopiuj
frm_Logowanie logowanie = new frm_Logowanie();
            Application.Run(logowanie);

            if (logowanie.zalogowano)
            {
                Application.Run(new frm_Glowne());
            }

W ten sposób przechodzimy do głównego form gdzie na starta mamy dynamiczne tworzenie menu (pobierane z bazy na podstawie uprawnień operatora) - tutaj już wykorzystuję

Kopiuj
Zmienne.DSNAdministracji

w której jest dostęp ODBC do bazy w której jest zapisane menu oraz Zmienne.IDOperatora

Kopiuj
 żeby sprawdzić uprawnienia.

W głównym form mamy tworzenie menu podczas Load:
```csharp
private void FormGlowny_Load(object sender, EventArgs e)
        {
                  
            //wczytanie menu głównego z bazy danych
            MenuStrip menuGlowne = new MenuStrip();
            this.Controls.Add(menuGlowne);
            this.MainMenuStrip = menuGlowne;
            OdbcDataAdapter da = new OdbcDataAdapter(Selecty_Menu.MenuGlowne, Baza_Polaczenie_Glowne.PolaczenieModuly);
            DataTable dt = new DataTable();
            Baza_Polaczenie_Glowne.PolaczenieModuly.Open();
            da.Fill(dt);

            foreach (DataRow dr in dt.Rows)
            {
                ToolStripMenuItem menuGlowneItem = new ToolStripMenuItem(dr["TekstMenu"].ToString());
                SubMenu(menuGlowneItem, Convert.ToInt32(dr["IDMenu"]));
                menuGlowne.Items.Add(menuGlowneItem);
            }
            
            this.MainMenuStrip = menuGlowne;
            Baza_Polaczenie_Glowne.PolaczenieModuly.Close();
        }

        //metoda wczytująca podmenu
        public void SubMenu (ToolStripMenuItem mnu, int submenu)
        {
            String Zapytanie = Selecty_Menu.MenuModuly_Podmenu1 + Zmienne.IDOperatora_zalogowanego + Selecty_Menu.MenuModuly_Podmenu2 + submenu;
            OdbcDataAdapter da_submenu = new OdbcDataAdapter(Zapytanie, Baza_Polaczenie_Glowne.PolaczenieModuly);
            DataTable dt_child = new DataTable();
            da_submenu.Fill(dt_child);

            foreach (DataRow dr in dt_child.Rows)
            {
                ToolStripMenuItem ssubmenu = new ToolStripMenuItem(dr["TekstMenu"].ToString(), null, new EventHandler(KliknieciePozycji));
                mnu.DropDownItems.Add(ssubmenu);
            }
        }

        //metoda kliknięcia w dane podmenu
        private void KliknieciePozycji(object sender, EventArgs e)
        {
            String ZapytanieGotowe = Selecty_Menu.MenuModuly_Klik_JakiForm + sender.ToString() + "'";
            OdbcDataAdapter datransaction = new OdbcDataAdapter(ZapytanieGotowe, Baza_Polaczenie_Glowne.PolaczenieModuly);
            DataTable dtransaction = new DataTable();
            datransaction.Fill(dtransaction);

            Assembly frmAssembly = Assembly.LoadFile(Application.ExecutablePath);
            foreach (Type type in frmAssembly.GetTypes())
            {
                //MessageBox.Show(type.Name);
                if (type.BaseType == typeof(Form))
                {
                    if (type.Name == dtransaction.Rows[0][0].ToString())
                    {
                        Form frmShow = (Form)frmAssembly.CreateInstance(type.ToString());
                        frmShow.MdiParent = this;
                        frmShow.Show();
                    }
                }
            }
        }

Do tego momentu wszystko działa prawidłowo, jest połączenie z bazą, zmienne są przechowywane poprawnie. Jak z tego poziomu przechodzę do kolejnego form podczas Load chcę wykorzystać

Kopiuj
Zmienna.DSNAdministracji

i tutaj mam problem bo jest ona pusta.

Kopiuj
 public partial class frm_TransWyjazdy : Form
    {
        public frm_TransWyjazdy()
        {
            InitializeComponent();
        }

        private void frm_TransWyjazdy_Load(object sender, EventArgs e)
        {
            MessageBox.Show(Zmienne.DSNAdministracji);
            MessageBox.Show(Convert.ToString(Zmienne.IDAdministracji));
            OperacjeNaBazie.WypelnienieDropDown(Selecty_Transport.TrasyDoWyjazdow, "TransTrasy_V", "IDTrasy", "NazwaPelna", cb_Trasa, Baza_Polaczenie_Glowne.PolaczenieModuly);
        }
}

MessageBox nic nie pokazują na na

Kopiuj
OperacjeNaBazie.WypelnienieDropDown

wywala błąd gdyż nie ma DSNAdministracji który jest potrzebny do Baza_Polaczenie_Glowne.PolaczenieModuly

Kopiuj
_13th_Dragon
  • Rejestracja: dni
  • Ostatnio: dni
0

Zmienne.IDOperatora_zalogowanego = this.IDOperatora;
zamień na:
Zmienne.Instance.IDOperatora_zalogowanego = this.IDOperatora;
Nie ma odwołania do Zmienne. bezpośrednio.
Tak a propos, konstruktor zrób prywatnym aby nie kusiło ...

DZ
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 115
0
_13th_Dragon napisał(a):

Zmienne.IDOperatora_zalogowanego = this.IDOperatora;
zamień na:
Zmienne.Instance.IDOperatora_zalogowanego = this.IDOperatora;
Nie ma odwołania do Zmienne. bezpośrednio.

Nie zrozumieliśmy się do końca. W moim rozwiązaniu które podałem w odpowiedzi na post Azariena nie używałem singleton stąd bezpośrednie odwołania bo zmienne były jako:

Kopiuj
public static class Zmienne
    {
        public static int IDOperatora_zalogowanego;
        public static int IDAdministracji;
        public static string DSNAdministracji; 
    }
_13th_Dragon napisał(a):

Tak a propos, konstruktor zrób prywatnym aby nie kusiło ...

Zrobiłem jako Private.
Zrobiłem również drugą opcję czyli zastosowałem po drodze singleton (wtedy tak jak wspomniałeś zmieniłem na

Kopiuj
Zmienne.Instance.IDOperatora_zalogowanego = this.IDOperatora;

itd - bez zmian - dalej przy wywoływaniu kolejnego form nie mogę uzyskać wartości zmiennej - jest pusta.
Zasugerowano mi również żeby spróbować wykorzystać jakiś kontener dependency injection do tego celu - czy ewentualnie ktoś może się wypowiedzieć na ten temat i pomóc w przetestowaniu tego rozwiązania?

_13th_Dragon
  • Rejestracja: dni
  • Ostatnio: dni
0

Jak chcesz i rybkę i pipkę to się nie da.

Kopiuj
public sealed class Zmienne
  {
   private static Zmienne instance = null;
   public int IDOperatora_zalogowanego;
   public int IDAdministracji;
   public string DSNAdministracji; 
 
   private Zmienne()
     {
     }
 
   public static Zmienne Instance
     {
      get 
        {
         if(instance==null)
           {
            instance = new Zmienne();
           }
         return instance;
        }
     }
  }

Odwołanie się: Zmienne.Instance.IDAdministracji

DZ
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 115
0

Wstawiłem dokładnie ten kod, odwołuję się poprzez Zmienne.Instance.IDAdministracji i nadal to samo, pusto w drugim form :(

somekind
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
0
Kopiuj
public sealed class Zmienne
  {
   private static Zmienne instance = null;
   public int IDOperatora_zalogowanego;
   public int IDAdministracji;
   public string DSNAdministracji; 
 
   private Zmienne()
     {
     }
 
   public static Zmienne Instance
     {
      get 
        {
         if(instance==null)
           {
            instance = new Zmienne();
           }
         return instance;
        }
     }
  }

Jest to przykład nieprawidłowej implementacji Singletonu, która jest niebezpieczna w środowisku wielowątkowym. Gdy wiele wątków jednocześnie wejdzie do if, to utworzonych zostanie wiele instancji tego singletonu.
Singleton (nie wymagający lazy loadingu) implementuje się tak:

Kopiuj
public sealed class Zmienne
{
   public int IdZalogowanegoOperatora;
   public int IdAdministracji;
   public string DSNAdministracji; 

    private static Zmienne_instance = new Zmienne();
    private Zmienne() { }
    public static ZmienneInstance
    {
        get
        {
            return _instance;
        }
    }
}

Tylko tu chyba nie w tym problem... Skoro wartości statycznych zmiennych statycznej klasy nagle znikają, to mamy tu do czynienia z cudem, albo gdzieś jednak są one nadpisywane. Trzeba wyszukać wszystkie ich wystąpienia w kodzie, i sprawdzić gdzie jeszcze są nadpisywane. Ewentualnie trzeba podebugować. W każdym razie jedno i drugie wymaga dostępu do całego kodu.

I to jest przykład problemu generowanego przez używanie publicznych pól. Gdyby były to prywatne pola, ustawiane w konstruktorze, udostępnione na zewnątrz jako właściwości tylko do odczytu, żadne cuda by się nie działy.

_13th_Dragon
  • Rejestracja: dni
  • Ostatnio: dni
0

Więc wstaw czujki (breakpoint) tam gdzie coś wpisujesz do IDAdministracji oraz tam gdzie "nadal to samo, pusto w drugim form" odpal program i zobacz czemu najpierw wywołujesz "nadal to samo, pusto w drugim form" a później tam gdzie coś wpisujesz do IDAdministracji.

DZ
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 115
0

Breakpointy niestety niewiele mi pomogły :(

Nie linczujcie mnie za to co zrobiłem (jak zaznaczałem, jestem "Newbie" ) - chciałem sprawdzić w którym miejscu pojawia się problem i do metody wywołującej kolejny form w którym zmienna już jest pusta wstawiłem w celu "debugowania" MessageBox wyświetlający po wykonaniu każdej linijce stan zmiennej:

Kopiuj
//metoda kliknięcia w dane podmenu
        private void KliknieciePozycji(object sender, EventArgs e)
        {
            MessageBox.Show("1: " +Zmienne.Instance.DSNAdministracji);
            String ZapytanieGotowe = Selecty_Menu.MenuModuly_Klik_JakiForm + sender.ToString() + "'";
            MessageBox.Show("2: " + Zmienne.Instance.DSNAdministracji);
            OdbcDataAdapter datransaction = new OdbcDataAdapter(ZapytanieGotowe, Baza_Polaczenie_Glowne.PolaczenieModuly);
            MessageBox.Show("3: " + Zmienne.Instance.DSNAdministracji);
            DataTable dtransaction = new DataTable();
            MessageBox.Show("4: " + Zmienne.Instance.DSNAdministracji);
            datransaction.Fill(dtransaction);
            MessageBox.Show("5: " + Zmienne.Instance.DSNAdministracji);
            Assembly frmAssembly = Assembly.LoadFile(Application.ExecutablePath);
            MessageBox.Show("6: " + Zmienne.Instance.DSNAdministracji);
            foreach (Type type in frmAssembly.GetTypes())
            {
                //MessageBox.Show(type.Name);
                if (type.BaseType == typeof(Form))
                {
                    if (type.Name == dtransaction.Rows[0][0].ToString())
                    {
                        Form frmShow = (Form)frmAssembly.CreateInstance(type.ToString());
                        MessageBox.Show("7: " + Zmienne.Instance.DSNAdministracji);
                        frmShow.MdiParent = this;
                        MessageBox.Show("8: " + Zmienne.Instance.DSNAdministracji);
                        frmShow.Show();
                        MessageBox.Show("9: " + Zmienne.Instance.DSNAdministracji);
                    }
                }
            }
        }

Aż do "8: " Zmienne.Instance.DSNAdministracji" wszystko jest ok, jest zwracana poprawna wartość zmiennej, potem następuje już wyświetlenie mojego nieszczęsnego form gdzie zmienna magicznie znika. Form ma następujący kod:

Kopiuj
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ModulyDodatkowe
{
    public partial class frm_TransWyjazdy : Form
    {
        public frm_TransWyjazdy()
        {
            InitializeComponent();
        }

        private void frm_TransWyjazdy_Load(object sender, EventArgs e)
        {
            MessageBox.Show("Wyjazdy: " +Zmienne.Instance.DSNAdministracji);
            OperacjeNaBazie.WypelnienieDropDown(Selecty_Transport.TrasyDoWyjazdow, "TransTrasy_V", "IDTrasy", "NazwaPelna", cb_Trasa, Baza_Polaczenie_Glowne.PolaczenieModuly);
        }
    }
}
Kopiuj
MessageBox.Show("Wyjazdy: " +Zmienne.Instance.DSNAdministracji);
  • już zwraca samo "Wyjazdy: "
    Zmienna ta jest wykorzystywana dalej w
Kopiuj
Baza_Polaczenie_Glowne.PolaczenieModuly
  • w tym przypadku jak jest pusta nie ma DSN połączenia z bazą po ODBC i program się wysypuje.

Jeśli nie widzicie niczego w kodzie co powodowałoby problem to może podpowiecie jak dokładniej zdebugować i znaleźć miejsce w którym zmienna jest "czyszczona" ??

somekind
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
0

O borze, czemu nie napisałeś od razu, że nie tworzysz okienek normalnie, tylko używasz refleksji?

Zapewne frmAssembly.CreateInstance tworzy okienko w innej AppDomenie, w której Twoja statyczna klasa jest niezainicjalizowana. Spróbuj użyć ntej metody: http://msdn.microsoft.com/pl-pl/library/ms224132%28v=vs.110%29.aspx i przekaż AppDomain.CurrentDomain jako pierwszy argument.

A tak w ogóle, to co próbujesz osiągnać? Czemu tworzysz okienka w tak zagmatwany sposób?

DZ
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 115
0

Napisałem :) W odpowiedzi na post Azariena podałem cały kod tworzenia menu i wywoływania później nowego form z niego :)

Już piszę "po co mi to" - generalnie założenie aplikacji jest takiej że ustawienia trzymam w bazie a dokładniej chodzi mi o uprawnienia. W bazie trzymam nazwę "modułu", do jakiej kategorii należy, w jakiej kolejności ma się wyświetlić w konkretnym menu, jaki form odpowiada za ten moduł i później do tego mam schemat uprawnień na prostej zasadzie id_usera, id_modulu, przegladanie, usuwania, dodawanie, edycja i jeśli jakiś user ma do danego modułu dostęp to wtedy widzi je w menu i może w dany moduł wejść. Program ma być dość rozbudowany (uzupełnienie system ERP w zakładzie produkcyjnym w którym pracuję).
Czy idę niewłaściwą drogą?? Co byś mi doradził żeby to zrobić "mniej zagmatwanie" ale żeby dodawanie nowych modułów (każdy moduł będzie osobnym form) nie było "karkołomne" (chodzi o tworzenie menu po dodaniu nowego form) - teraz tworzę nowy form, daję mu nazwę, wpisuję ją do bazy i nie muszę się martwić o menu że gdzieś zapomnę dodać itd.

somekind
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
0
  1. Zrób normalny program, z normalnym okienkiem głównym posiadającym normalne menu, które tworzy normalne okna (poprzez new FormJakiśTamDlaPaniKsięgowejZosi()).
  2. Uprawnienia odczytuj z bazy, a następnie na ich podstawie ukryj (albo pokaż) opcje w menu.
    Będzie 100 razy wydajniej, czytelniej i bezpieczniej. Dodanie nowej pozycji do menu nie jest raczej czasochłonne, a już na pewno nie tak bardzo jak walka z błędami, które powoduje obecne automagiczne podejście. ;)

teraz tworzę nowy form, daję mu nazwę, wpisuję ją do bazy i nie muszę się martwić o menu że gdzieś zapomnę dodać itd.

No i gdzie tu zysk? Tak czy siak musisz pamiętać o dodaniu okienka do bazy, równie dobrze możesz je dodawać do menu.

Nie wiem też, czy Twoja struktura tabeli uprawnień jest dobra. Nie wygodniej podzielić użytkowników na grupy, a następnie nadawać uprawnienia całym grupom?

DZ
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 115
0

Z tymi uprawnieniami na grupach będzie problem bo temat jest bardzo indywidualny - praktycznie nie ma w tej chwili dwóch userów którzy mieliby mieć te same prawa ale myśląc przyszłościowo przemyślę temat zmiany struktury bazy uprawnień. Zrobię test z menu wprowadzanym w programie. Masz jakiś sprawdzony sposób na ukrywanie/pokazywanie opcji? Jakich właściwości użyć jako identyfikatora "modułu" żeby połączyć to z moją tabelą uprawnień?

somekind
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
0

Jaki problem? Wystarczy jedna dodatkowa tabela na grupy.

Ostatniego pytania nie bardzo rozumiem. Ja bym zrobił coś mniej więcej takiego:

Kopiuj
if(użytkownik.MaRolę(RoleSystemowe.Ksiegowy))
{
    this.menuKsiegowosc.Visible = false;
}

Przy czym RoleSystemowe do enum, który jest zapisywany np. do tabeli użytkowników w bazie.

DZ
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 115
0
somekind napisał(a):

Ostatniego pytania nie bardzo rozumiem.

Przypuszczam że jako początkujący nie umiem tego jeszcze fachowo opisać a nie chcę też pisać bzdur żeby uniknąć linczu na forum :)
Chodzi mi o to żeby zrobić na zasadzie wykorzystania jakiejś właściwości w menustrip np. tag (teoretyzuję bo nie wiem czy jest taka opcja), tam każdemu menu nadać coś na styl ID_Modulu i takie same ID_Modulu w tabeli uprawnień (temat ról "Księgowy" itd nie przejdzie) i później odpowiedni mechanizm który przy wczytywaniu głównego form pobiera ID_Modulu do których user ma prawa i na tej podstawie odwołuje się do tag-a i ustawia visible na true w menu do modułów do których user ma prawa.

somekind
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
0

Rozumiem, w takim razie możesz użyć właściwości Tag. Tylko ja bym nie operował na jakichś "ID", lecz zrobił sobie enum zawierający wszystkie role w systemie, i ten enum zapisywał zarówno do bazy danych, żeby określić uprawnienia użytkownika, jak i wstawiał do Tag, żeby określić, które role mają mieć widoczną daną opcje w menu.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.