Ošetření chyb na úrovni třídy či celé aplikace   zodpovězená otázka

VB.NET, Architektura, .NET

Chtěl bych se zeptat, zda je možné ošetřit chybu připojení k SQL serveru na nějaké vyšší úrovni než pouze na jednotlivé proceduře resp. události ?

Používám Linq dotazy a samozřejmě při loadu uvodního vstupního formuláře kontroluji zda je SQL server k dispozici jednoduchým testem připojením k tabulce a použítím Try, Catch, End Try.(nechápu k čemu je dobrý Finally, pro přehlednost ?)

Ale co když dojde k výpadku v již "rozběhnuté" aplikaci, to musím u každé události ošetřit tento možný výpadek ? Lze naprogramovat obsluhu této chyby pro celou aplikaci či alespoň třídu, bez nutnosti se odkazovat na ošetřující rutinu v každé události či proceduře ?

Předem děkuji i za negativní odpověď, ať nemarním čas nad něčím co nelze uskutečnit.

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Finally je dobré k tomu, že kód v něm vložený se provede vždy (tzn. ať už dojde k chybě nebo ne). Například někde si ošetříte nějákou chybu a pokud to bude krytická chyba, budete třeba chtít aby se aplikace uzavřela. Aplikace se bude zavírat z jiné procedury, ale pokud umístíte do finally kód na uzavření spojení, tento kód se provede ještě než aplikace skončí.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

díky za vysvětlení. Ale v tom případě spojení uzavřu rovnou v Catch nebo tam nastavím odkaz na proceduru která to provede ? Naopak ve finally se mi to uzavře i když žádná chyba nevznikne, protože se ten kód provede, jak jste psal a i jak jsem to testoval pokaždé. Takže Finally má stejný smysl, jako když to napíšu pod End Try a použiju z VB6 error resume next ? :-)

Ale jestli se to provede vždy i když v jiné proceduře dojde k uzavření např. vlastního programu, tak se to dá možná uplatnit, ale zase se musí ošetřit, aby to "pracovalo" jen když dojde k chybě.

Pochopil jsem to správně ?

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

No jde o to že pokud nedojde k chybě, tak provedete něco nad databází a spojení uzavřete. Pokud dojde k chybě, provedete třeba něco jiného a spojení uzavřete. Takže proč to psát 2x , když to stačí dát jednou do finally:) Zkrátka a prostě do finally se dávají částí kódu který se má provést bez ohledu na to zda došlo k chybě nebo ne.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Finally se používá v případě, kdy je třeba garantovat provedení nějakého kódu před návratem z metody. Příklad:

Public Function Sum(ByVal x As Integer, ByVal y As Integer)
  Dim errorMessage As String
  Try
    errorMessage = String.Empty
    Return x + y
  Catch ex As Exception
    errorMessage = ex.Message
    Return 0
  Finally
    'Tento kód se provede ještě před Return
    Debug.WriteLine(errorMessage)
  End Try
  'Kdybyste dal Debug.WriteLine sem, neprovede se
End Function

Co se týče vašeho dotazu tak ano, měl byste použít Try...Catch ve všech metodách, kde předpokládáte že by mohlo dojít k nějaké vyjímce kterou můžete ošetřit. Globální handler se používá právě pro zpracování NEOŠETŘENÝCH vyjímek.

'Přidat globální handler vyjímek
AddHandler AppDomain.CurrentDomain.UnhandledException, AddressOf GlobalHandler

Sub GlobalHandler(sender As Object, e As UnhandledExceptionEventArgs)
  'Ošetřit vyjímky na globální úrovni
End Sub
nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Díky moc.

a)takže finally vlastně funguje jako On error resume next ve VB6 s tím že je to přehlednější :-)

b)Ten globální handler se tedy musí vložit do každé procedury a kam, přímo do "Catch" při vzniku chyby ?

Zkusím se někam podívat, ale kdyby jste měl čas mohl by jste ukázat nějaký příklad či odkaz ? Já nevím jak s tímto konkrétně pracovat, teprve s .net začínám.

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

a) Ne, Finally nefunguje jako On Error Resume Next, oba příkazy fungují zcela odlišně.

b) Globální handler je určen pro odchytávání vyjímek na globální úrovni, tedy v rámci aplikační domény. Jakákoliv neošetřená vyjímka skončí v tomto handleru. Ten se přiřazuje v drtivé většině případů v proceduře Main (ve Windows Forms aplikaci to jde přiřadit ve vlastnostech projektu - View Application Events).

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Když to tedy shrneme, stačí vlézt do vlastností projektu, na tuším že první záložce najít tlačítko Application Events a kliknout na něj. Vygeneruje se nová třída, ve které můžete ošetřit událost UnhandledException. Tam spadnou všechny výjimky, které nebudou ošetřené, tudíž ty, které nebudou v žádném Try..Catch..End Try bloku.

Finally má valný smysl jen v případě, že máte Try bloky zanořené. Dejme tomu funkce 1 volá v Try bloku funkci 2, která má v sobě opět Try blok. Ve funkci 2 nastane chyba a provede se větev Catch. Buď v této větvi nechytíme zrovna tu výjimku, která zrovna nastala (protože chytáme jen jiné typy), a tím pádem nám výjimka "propadne" ven do funkce1, kde ji zachytíme, nebo v Catch bloku tuto výjimku chytíme, ale protože nemůžeme nic dělat, vyhodíme novou, která opět propadne nahoru. I v této situaci se provede Finally a tady je jeho použití nutné, jinak opravdu stačí dát kód místo do Finally až za End Try.

Ona celá koncepce Finally není úplně šťastná, je hezké, že v něm správně máme zavírat spojení nebo streamy, ale v praxi je tam pár nepříjemností. Klasicky napíšu Try, v něm si vytvořím nějaký FileStream a čtu z něj atd. Když nastane chyba, můžu provést nějaké ošetření, ale ve Finally nemůžu ten stream jen tak zavřít. Pokud totiž chyba nastane už při vytváření streamu, tak ve Finally spadnu na NullReferenceException. Jasně, je to jedna podmínka, ale otravuje to. A navíc pokud si tu proměnnou se streamem nadeklaruji až v Try bloku, vůbec ji ve Finally neuvidím, protože má platnost jen v tom Try. Kdyby aspoň fungovala i v tom Finally a Catch, to by bylo úžasné. Takhle ji musím deklarovat mimo.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Po stisknutí View Application Events se "cosi" stvoří, ale nemá to žádný název. Vytvoří se jen nějaký code file. Když do něho vložím výše uvedenou proceduru pak následuje:

Error 1 Statement is not valid in a namespace.

Error 2 Name 'GlobalHandler' is not declared.

Nevím jestli to má být třída pod určitým názvem a tedy jakým ?

Zkoušel jsem třídu pojmenovat Main,ApplicationEvent,Class1 a stejně ho ten "GlobalHandler" nedokáže nalézt

Ještě mi není jasné kam se má ten "AddHandler" umísťovat a jaká je jeho platnost. Stačí ho načíst při nahrání úvodního formuláře (tak jsem to udělal) a bude jeho platnost pro všechny ostatní procedury, funkce, formuláře resp. třídy ?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Tak po smazání třídy se už nová vygenerovala s názvem, ale chyba deklarace přetrvává. jak se deklaruje GlobalhHandler a kde?

Error 1 Name 'GlobalHandler' is not declared.

Předem děkuji

Zde je vygenerovaná část

Namespace My

    ' The following events are available for MyApplication:
    ' 
    ' Startup: Raised when the application starts, before the startup form is created.
    ' Shutdown: Raised after all application forms are closed.  This event is not raised if the application terminates abnormally.
    ' UnhandledException: Raised if the application encounters an unhandled exception.
    ' StartupNextInstance: Raised when launching a single-instance application and the application is already active. 
    ' NetworkAvailabilityChanged: Raised when the network connection is connected or disconnected.
    Partial Friend Class MyApplication
        Public Sub GlobalHandler(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs)
            'Ošetřit vyjímky na globální úrovni
        End Sub
    End Class

End Namespace
nahlásit spamnahlásit spam 0 odpovědětodpovědět

Člověče vy vůbec nevíte která bije. Ten můj kód byla pouze ukázka jak to napsat, ne žádný funkční příklad. Jakmile zobrazíte ten kód vytvořený designérem (View Application Events ve vlastnostech projektu), vyberte v levém horním seznamu (MyApplication Events) a v pravém horním seznamu UnhandledException. Automaticky se vám vytvoří handlovací procedura včetně přiřazení k události.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Máte pravdu. Dělal jsem před lety ve VB6 a hodně používám VBA hlavně v accessu. Nyní jsem se pustil do (m)účení VB.net, ale nějak špatně se v tom zatím orientuji. Proto se ptám a jsem rád když mi někdo poradí. Je pravda, že se učím stylem pokus omyl a v určitých věcech jsem vedle jak ta jedle. V práci jsme měli všechno v Accessu a to na síti není zrovna ideální řešení. Tak se snažím pochopit nové technologie. Zatím tápu především v následujícím:

1. Globální ošetření chyb (nechce se mi dělat ošetření v každé procedůře pokud dojde k výpadku serveru). Prostě nechápu jak se ty handlery deklarují a vůbec jak to vlastně celé pracuje.

2. Pohyb v datagridview, ve VB6 to bylo úplně jiné a podle mě jednodušší. Nechápu proč to není řešené např. Datagrid1.cell(x,y).select

3. Zjistit jak inteligentněji komunikovat s SQL serverem. Zatím umím jen tahat data ze serveru a dále je zpracovávat pomocí LINQ, ale nemohu přijít, jak by sám server odeslal informaci aplikaci, že došlo ke změně jeho dat. Zatím to řeším tak, že mám trigger, který sleduje události v důležitých tabulkách a pokud dojde ke změně, zapíše čas této změny do malé jednoduché tabulky. Ostatní aplikace se v časových intervalech dívají do této tabulky a pokud je čas v tabulce čerstvější než poslední update, tak si aplikace stáhne aktualizovaná data. Vím že to není ideální, ale neumím napsat takovou knihovnu resp. assembly a opět absolutně netuším jak se poté takové propojení deklaruje.

Ale něco pozitního nakonec, ačkoliv jsem před dvěma měsící vůbec neměl tušení o SQL serveru ani o VB.net, tak aplikace pracuje k mé spokojenosti a jen potřebuji vyřešit tyto tři problémy. No bez toho datagridu se obejdu, ale to ošetření chyb budu muset nějak vyřešit. V nejhorším budu při každém LINQ dotazu ověřovat stav připojení, ale přijde mi to "hloupé" a pracné.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

1) Ošetřovat vyjímky v každé metodě je běžná praxe, ven z metody pustíte pouze vyjímky, které nemůžete ošetřit přímo uvnitř metody. Ty které pustíte ven z metody se buď chytí o úroveň výš nebo v globálním handleru pokud už žádná vyšší úroveň není.

2) DataGridView má vlastnost Item, jejíž dva parametry (sloupec a řádek) určují požadovanou buňku.

3) SQL server vás neupozorní na nějakou změnu dat, takhle to nefunguje. Skutečně se to dělá tak, že v pravidelných intervalech kontrolujete jestli se něco změnilo. Teoreticky by šla udělat Assembly která by se vložila do databáze a její metody by volal nějaký trigger. Metody této Assembly by potom nějakým způsobem (.NET Remoting/WCF apod.) upozornily klientské aplikace na změnu dat. Ovšem to je dost komplikované.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Děkuji za rady.

1)Mám takové podezření, že to globální zpracování chyb, není funkční v ladicím prostředí. Možná proto nechápu jak to pracuje, protože se mi ta úmyslná chyba zachytí vždy už ve Visual studiu a nazpracuje ji tedy jako "neodchycenou". Je to možné ? Pokud ano, jak nastavit VS, aby chyby nezachýtavalo neošetřené chyby už ve vývojovém prostředí ?

Samozřejmě se snažím chybám vyvarovat a ošetřít je už na úrovni jednotlivých procedur, ale v případě výpadku spojení ze serverem, bych musel kvůli této možnosti, psát opakovaně skoro pořád stejný kus kódu, byť by se jednalo jen o odkaz na funkci, která by ztrátu připojení řešila.

2) Identifikovat buňku se dá více metodami, ale jak ji vybrat, aby tam byl kursor, aby měla fokus ?

(DataGridView1.Item(2, 1).???)

Něco jako TextBox1.Focus() ale pro určitou buňku

3) Nemáte odkaz na nějakou stránku, kde se dá k tomu něco zjistit ? Mám knížku T-SQL SQL server 2005, kde je uveden i příklad na vytvoření assembly, ale tam je jako příklad uvedeno vytvoření textového souboru (log), pomocí knihovny na kterou se SQL server naváže, ale není tam popsán, pro mě důležitý způsob komunikace mezi serverem a aplikací. Problém je v tom, když více uživatelů vidí neaktuální data, k čemuž při použití timeru, může dojít, nemusí být daná operace už žádoucí. Nemluvě o zbytečném zatížení sítě a serveru.

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.
  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback