Přidat otázku mezi oblíbenéZasílat nové odpovědi e-mailemVyřešeno Architektura formulářové aplikace C#

Ahoj,

před nějakým časem jsem se tady ptal, jak navrhnout aplikaci.
Původně se mi moc nechtělo, ale uposlechl jsem Wikanův návrh.

V podstatě jde o databazovou aplikaci, a vytvářím základní objekt, který bude nějak spravovat tok dat mezi formuláři a databází.

Co je hotové, je objekt, který spravuje naplnění formuláře. Dále by zde mělo fungovat i uložení + případně INZERT údajů do DB.
Na uložení jsem se trochu zasekl, a sem směřuji můj dotaz.
Zatím uvažuji jen editaci či vkládání jednoho záznamu,
Zatím objekt tedy vypadá nějak takto:

class CSshowFillClass
    {

        public System.Windows.Forms.Form form;
        string table;
        string columnName;
        int id;
        // konstruktor
        public CSshowFillClass(System.Windows.Forms.Form form, string table, string columnName, int id = 0)
        {

            this.form = form;
            this.id = id;
            this.columnName = columnName;
            this.table = table;


            SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["aplikacka_NET.Properties.Settings.ConnectionString"].ConnectionString);
            conn.Open();
            SqlDataReader rdr = null;

            SqlCommand cmd;

            if (id != 0)
            {
               cmd = new SqlCommand("SELECT * FROM aplikackaDB.dbo."+ table +" where "+ columnName  +" = " + id.ToString(), conn);
            }
            else
            {
                return; 
            }
                       
            rdr = cmd.ExecuteReader();
            
                while (rdr.Read())
            {

                foreach (Control ctrl in this.form.Controls)
                {
                    string ControlName = ctrl.Name.ToString().Substring(3, ctrl.Name.ToString().Length - 3);      

                    if (ColumnExists(rdr, ControlName))
                    {
                        ctrl.Text = rdr[ControlName].ToString();   //   NAPLNENI TEXTOVÝCH POLÍ HODNOTAMI Z DB
                    }
                    string controlType = ctrl.GetType().ToString();
                }
            }
            
            
        }

        public bool ColumnExists(IDataReader reader, string columnName)
        {
            for (int i = 0; i < reader.FieldCount; i++)
            {
                if (reader.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
                {
                    return true;
                }
            }

            return false;
        }

        public bool saveForm()
        {
            SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["aplikacka_NET.Properties.Settings.ConnectionString"].ConnectionString);
            conn.Open();
            SqlCommand cmd;
            cmd = new SqlCommand("SELECT * FROM aplikackaDB.dbo." + table + " where " + columnName + " = " + this.id.ToString(), conn);
            SqlDataReader rdr = cmd.ExecuteReader();
            string columnsNames = String.Empty;

            while (rdr.Read())
            {
                foreach (Control ctrl in this.form.Controls)
                {
                string ControlName = ctrl.Name.ToString().Substring(3, ctrl.Name.ToString().Length - 3);      //str.Substring(10, str.Length-10)

                if (ColumnExists(rdr, ControlName))
                    {
                        columnsNames += ControlName + "= '"+ ctrl.Text   +"'," ; 
                    }
                string controlType = ctrl.GetType().ToString();
                }
            }
            
            columnsNames = columnsNames.Remove(columnsNames.Length - 1);
            rdr.Close();
            cmd = new SqlCommand("UPDATE aplikackaDB.dbo." + table + " SET "+ columnsNames +" WHERE " + columnName + " = " + this.id.ToString(), conn);
            cmd.ExecuteNonQuery();

            return true;
        }

        public void ShowForm()
        {
            form.Show();
        }

   }

Potíž mám ale s jeho použitím.
Konkrétně objekt přebírá tato data: ukazatel na formulář, název DB tabulky, název sloupce, ID řádku (záznamu)

Když použiji v hlavním těle následující konstrukci:

Smlouvy formSmlouvy = new Smlouvy();
            CSshowFillClass csshowFillClass = new CSshowFillClass(formSmlouvy, "Smlouvy", "IDNAVRH", 245);
            formSmlouvy.Show();

Tak se formulář bez problému naplní daty a zobrazí. Potíž mám ale v tom, jak nyní tato data uložit (UPDATE) v DB.

Protože pokud nechám proběhnout např. tento kód:

   Smlouvy formSmlouvy = new Smlouvy();
            CSshowFillClass csshowFillClass = new CSshowFillClass(formSmlouvy, "Smlouvy", "IDNAVRH", 245);
            formSmlouvy.Show();
            csshowFillClass.saveForm(); 

Tak se kvůli fungování Windows (WIN32 API) nejprve provedou negrafické operace a teprve pak se form zobrazí.
Takže kód nejde "postupně".

Co navrhujete?

Předmět Autor Datum
V prvom rade si treba uvedomiť, že keď programuješ UI aplikáciu, tak väčšinou obsluhuješ udalosti. T…
los 22.10.2017 00:34
los
A mapovanie z tohto modelu do jednotlivých formulárových polí by si mal zabezpečiť samotný formulár,…
Flash_Gordon 22.10.2017 06:19
Flash_Gordon
Form má událost Closing, kde si to ukládání můžeš ošetřit.
Wikan 22.10.2017 09:45
Wikan
Z našich zkušeností s velkým informačním systémem, zvlášť když budeš mít hodně podobných formulářů -…
Jan Fiala 22.10.2017 10:29
Jan Fiala
Děkuji ! To zní nejrozumněji, prodiskutuji to. poslední
Flash_Gordon 22.10.2017 11:29
Flash_Gordon

V prvom rade si treba uvedomiť, že keď programuješ UI aplikáciu, tak väčšinou obsluhuješ udalosti. Takže ak chceš, aby sa nejaký kód vykonal až vtedy, keď sa formulár zobrazí, tak ten kód umiestni do obsluhy udalosti Form.Shown.

Pokiaľ potrebuješ vykonávať nejaké dlhšie trvajúce operácie tak, aby si neblokoval UI vlákno, tak ich musíš vykonávať na pozadí, t.j. na inom vlákne. Vtedy treba myslieť na to, že k formulárovým prvkom nesmieš pristupovať z iného než UI vlákna. Na toto sa kedysi používal BackgroundWorker, dnes by si si mal vystačiť s async/await.

Ináč pokiaľ chceš riešiť nejakú architektúru aplikácie, tak by si rozhodne nemal miešať UI kód s SQL kódom. V normálnej aplikácii by som čakal, že volanie SQL príkazov pre zmluvy bude v samostatnej triede, napr. SqlContractProvider (odporúčam používať všade angličtinu a nemiešať do toho české slová). Tá by mala načítané hodnoty z databázy uložiť do modelu, t.j. triedy Contract, ktorá reprezentuje samotnú zmluvu. A mapovanie z tohto modelu do jednotlivých formulárových polí by si mal zabezpečiť samotný formulár, nie nejaká pomocná trieda, ktorá v podstate ani nevie, s akým presne formulárom vôbec pracuje.

Pri objektoch, ktoré implementujú rozhranie IDisposable, vysoko odporúčam používať using, aby sa alokované prostriedky uvoľnili aj v prípade, že nastane nejaká chyba.

A mapovanie z tohto modelu do jednotlivých formulárových polí by si mal zabezpečiť samotný formulár, nie nejaká pomocná trieda, ktorá v podstate ani nevie, s akým presne formulárom vôbec pracuje.

Ano, :-) takto jsem to původně udělal, ale nepříjemnost pak nastala asi jinde.
V DB je opravdu mnoho tabulek, a uvažujeme tedy udělat universální objekt, který vyplní pole ve formuláři na základě shody jmen (formulářové pole obsahuje jméno sloupce v DB).

Výše popsaná část funguje. Jde mi teď, ale o to, když uživatel "zavírá" nějaký formulář s daty tabulky, aby se mohl ještě nějak rozhodovat, zda změny uložit.

V .NETu lze volat namísto form.Show() form.ShowDialog(), což zajistí také čekání na další kód.

Jak by jsi řešil v takovém případě to rozhodnutí uživatele o ukládání ?

Z našich zkušeností s velkým informačním systémem, zvlášť když budeš mít hodně podobných formulářů - editace jednoho záznamu:
udělej si předka formuláře a objektu, které budou mít základní metody.

Předek objektu bude mít metody na načtení uložení, vyhledání, smazání,m příp. tisk.
Předek formuláře bude mít jeden (nebo více) základních editů a bude volat metody objektu. Metody pro vymazání prvků, naplnění, vyhledávání, validaci, nastavení přístupnosti a uložení
Po změně hlavního kódu se ověří, zda záznam existuje a podle toho se buď načte existující záznam nebo vyhledává nebo edituješ nový záznam.

Pak jen vytváříš potomka objektu a formuláře a přepíšeš několik metod. Vývoj půjde rychleji a hlavně bude standardizovaný, takže opravy se dají dělat rychle mechanicky, pokud nepostačí opravit pouze předky.

Zpět do poradny Odpovědět na původní otázku Nahoru