Tja! Hej Jimmy, Tanken med exceptions är att de skall användas där de behövs, alltså när något oväntat inträffar. Att en emailadress inte är korrekt eller kanske redan finns är inte ett oväntat resultat, då detta är något som jag som programmerar kan förvänta mig. Hej! Tackar för intressnat information. "Jag får nog säga att jag loggar hällre alla fel som uppstår även om det kanske inte är direkt ett fel(kanske bara en konstig utgång). Detta gör det avsevärt lättare att hitta konstigheter. När systemet är inkört kan man ju ta bort delar av loggningen om man så önskar. Men i ett system som fungerar som det ska så bör det ju inte läggas någon raporterings data i loggen(ingen Exception) " Exakt jag inte inte bara ute efter att logga själva felet utan även data som använts när felet uppstod. Ex så kan det vara vem som är inloggad, vilken sida man befinner sig på, vilka val som är gjorda, vilken klass och funktion där felet uppstår. Den mesta av den information som du efterfrågar kan du få ut ifrån exceptions-objectet. Så som Methodnamn, linenumber. Username kan du få från Environment.Username. Hmm?? varför tror du jag skulle skriva min try and catch på det där viset? Jag är mycket införstådd med att man ska se till att hantera alla fel som man fångar, antingen genom att låta applikationen hantera det så väl det går eller logga felet om det inte finns annant utgång. "varför tror du jag skulle skriva min try and catch på det där viset? " Exakt Steg två låter bra för min del =)Felhanterings lager?
Jag undrar lite hur man bör hantera fel som uppstår i applikationer.
Säg att vi har Application Lyer(PL), Business Logic Layer(BLL) och Data Access Layer(DAL).
Jag brukar använda mig av en bubblings-teknik så om ett fel inträffar i DAL, så kastas(throw) det upp till BLL som vidare kastar det upp till PL.
I PLs try and catch så registreras felet i en databas och/eller visas på skärmen(try, catch, finnaly).
Problemet med detta kan dock vara följande:
Säg att man har byggt en funktion i DAL som tar in fel medelande, användar Id, inparamertar och massa annat, och lagrar ner detta separat i databasen. Tackvare detta lagras separat i databasen så kan man lättare göra sökningar på det vid senare skede.
Säg då vidare att det uppstår ett problem i DAL eller BLL då kastar vi ett Exception(ex throw(ex) till PL). När vi då kommer upp i PL och ska registrera felet så har vi kanske inte tillgång till viss infor, ex inparametrar.
Kanske är det bättre om varje exception som inträffar tas handom där den händer. Så om det inträffar ett fel i BLL så registrerar den felet direkt med DAL och returnerar då null eller false(som då visar att ett fel har uppstått för den funktion som kallade på nuvarande). Samma gäller då för PL som också själv kontaktar DAL för att skicka sitt fel medelande. Men då kanske det inte är en skiktad lösning längre? Sv: Felhanterings lager?
Det här är verkligen ett område det varken finns rätt eller fel. Men det finns kanske mer eller mindre bra lösningar.
Vi tar Infoga ny användare som exempel, såhär brukar jag göra:
Jag delar upp fel i olika kategorier. Typ Critical, Error, Warning och Info
PL samlar ihop all information och validerar det som går och anropar sedan UserFacadeBLL.AddUser(User u)
I metoden AddUser Validerar jag all information, t.ex. att epostadressen är korrekt. Om den inte är det kastar jag ett fel. throw new ArgumentException("UserFacadeBLL.AddUser: Incorrect format on emailadress. Value = " + u.EmailAdress);
I PL Fångar jag detta undantag och visar ett felmeddelande och loggar det på nivå Info. ErrorHandlerBLL.LogError(ErrorLevel.Info, exception);
Om allt som går att validera i BLL är korrekt skickas den vidare till DAL.
UserStorageDAL.AddUser(User u);
Om det där upptäcks att det redan finns en användare med den här adressen kastar jag ett nytt undantag.
throw new ArgumentException("UserStorageDAL.AddUser: Email does already exist. Value = " + u.EmailAdress);
När jag fångar det här undantaget i BLL blir det såhär:
catch(ArgumentException ex)
{
throw new ArgumentException("UserFacadeBLL.AddUser: Exception when calling UserStorageDAL.AddUser", ex);
}
När det nya felet kommer till pl, skicka till ErrorHandlerBLL.LogError(ErrorLevel.Info, exception); och visa felmeddelande. Det som händer i Errorhandler är att den nystar upp alla fel och loggar dem så man ser exakt vart felet uppstod och med vilka parametrar, dessutom ser du vilken väg felet har tagit ner från PL.
Vissa anser att fel av den här typen är onödiga men jag tycker det funkar bra för mig. Detta går att tillämpa för alla typer av fel, det viktiga är att man så sällan som möjligt fångar basklassen Exception utan de mer specificerade barnen.
Det som är viktigt att poängtera är att det går att förtydliga massor här genom att använda egendefinierade exceptions hela vägen. T.ex. EmailExistsException. Mycket snyggare, lättare att förstå och med snippets tar det max 20sek att göra. En annan lösning är att skicka en egendefinierad Enum som out parameter. T.ex. AddUser.EmailExist eller AddUser.Success och bara hantera "riktiga fel" som att någon har dragit ur sladden ur databasservern.
Jag hoppas du har blivit iaf något klokare av det här!Sv: Felhanterings lager?
Exempel på vanliga exceptions är "file not found", detta är något som man som programmerar kan kontrollera själv om filen finns eller ej. Anledning till att minska ner på antalet exceptions är helt enkelt prestanda, att kasta ett exceptions kostar massor jämfört med en check och sedan true/false tillbaka. Nu kanske det inte är något kritiskt om man har det i sitt klientUI på någon windows klient. Men om man börjar använda exceptions för att kontrollera olika saker i sin businesslager som delas av flera användare så kan prestandaförsämringarna märkas.
Ut över det så ser jag ingen som helst mening med en try-catch runt ett kodsnutt om du ändå inte kommer hantera felet som uppträdder. Att göra en try-catch och sedan bara kasta felet vidare ger ingen större mening, det skall vara om man utifrån det exception som man fångar här, kan skapa ett nytt eget exception och längre upp i kjedjan så kontrollerar man vilka egna exceptions som kastas och gör olika saker med.
Nu har jag byggt en egen loggings komponent som kan logga exceptions med dess innerexceptions ner i databasen och samtidigt binda ihop alla dessa exceptions med ett groupexceptionsid, vilket gör det lite menningslöst att logga där felet uppstod, utan jag sköter allt sånt central, vilket är skönt när man gör förändringar i logkomponenten, ett ställe att ändra på i min applikation.
Men som sagts tidigare du kommer få nästan lika många olika svar som du får svarande, gör på det sättet som du tycker verkar bäst, det finns för- och nackdelar med att använda exceptions i tid och otid, framför allt så får man ju ett enklare sätt att hantera sin felhantering på. Det som joakim skriver är väldigt smidigt och enkelt sätt att bygga felhantering på, men i fleranvändare system så kommer det kräva prestanda...
- MSv:Felhanterings lager?
mycket intressant område att klura på :-)
Jag rekomenderar att titta igenom följade artikel
http://www.blueskyline.com/ErrorPatterns/A2-LongshawWoods6.pdf
går igenom några designmönster för just felhantering
//DSv:Felhanterings lager?
Min felhantering liknar den som joakim nämner, men vissa mindra ändringar. Ex så händer det något nere i l DAL så tar den handom o loggar felet direkt, för stället att bubbla upp det till toppen som därefter skickar ner det till DAL igen för databas loggningen.
Frågan är ny om man kan kanske göra en egen exception klass som tar in feler parametrar.
Som jag nämnde tidigare så vill man i vissa fall logga även de värden som orsakar felet, detta går så klart att lägga in i en vanlig Exception string men det blir rörigt och svårt att söka på.
Så om man bygger en Exception klass som har flera olika kontruktuers som tar emot olika info så kan vi lägga in alla info som krävs om ett fel uppstår i ex BLL.
Å andra sidan vet jag att Microsoft har skapat en del block och ett av dessa ska vara till för Exception logging, dock har jag inte taget någon närmare titt på det än.
Magnus Gladh:
Jag får nog säga att jag loggar hällre alla fel som uppstår även om det kanske inte är direkt ett fel(kanske bara en konstig utgång). Detta gör det avsevärt lättare att hitta konstigheter. När systemet är inkört kan man ju ta bort delar av loggningen om man så önskar. Men i ett system som fungerar som det ska så bör det ju inte läggas någon raporterings data i loggen(ingen Exception)
Sen att köra try and catch för att göra sina valideringar verkar inte så bra. Som du säger så tar de ganska mycket prestanda.Sv: Felhanterings lager?
Nu blandar du ihop exceptions med annan avlusningsinformation.
Om du får ett fel i ditt DAL och låter detta bubbla upp genom alla din lager, där du loggar vid varje try-catch du har, så kommer du ha exakt samma information som om du inte har några try-catch och fångar felet i unhandleexception. Du får inte mer information om exception bara för att du loggar längre ner.
Att du sedan vill logga annan information så som, debuging data, och att du kanske i produktion vill stänga av dessa loggningsfunktioner har ju inget att göra med din exceptions. Det kan kanske vara så att du vill som du nämnde logga ner en "view" av det objekt/input data som orsakade felet, men då gör du mer än bara en try-catch-throw, nu lägger du logik i din catch-sats och då kan du inte låta bli att hantera felet eftersom den information som du vill kommer gå förlorad i de övriga lagerna.
- MSv:Felhanterings lager?
I vissa fall så kanske man inte har tillgång till alla datan där felet upptäcks. Så om vi säger att ett fel uppstår i DAL och vi vet inte här vem som är inloggad. Då vill jag skapa ett Exception som innehåller den data som funktionen i fråga har fått in, alltså all den datan som vi har tillgång till och som kan vara orsaken till ett fel. Sen vill jag bubbla denna Exception upp till BLL där jag kanske har information om vem det är som är inloggad eller liknande, vidare vill jag slutligen att den ska bubbla upp till PL då all loggning till databasen förs i PL.
Vad tror ni/du om detta? är det fel vis att bygga på? Anledning till att jag vill bubbla det uppåt är inte bara för att få tag i information på vägen utan för att standardisera det hela lite så att Exceptions alltid loggas i PL.
//JimmySv: Felhanterings lager?
Titta på Log4Net de samlar upp det mesta av den information som du behöver automatiskt ifrån exceptionobjektet. Den kan dock inte hämta ren datainformation, så som värdet på en variable, det måste du hantera själv och det måste göras i din try-catch.
"Anledning till att jag vill bubbla det uppåt är inte bara för att få tag i information på vägen utan för att standardisera det hela lite så att Exceptions alltid loggas i PL."
Bubbla eller inte, det får du avgöra själv bara man slipper se följande kod.
<code>
try{
...
}catch(Excepetion exception)
{
Log(exception);
throw;
}
</code>
eller ännu värre
<code>
try{
...
}catch(Excepetion exception)
{
throw;
}
</code>
Det finns ingen som helst menning med sådan kod. Gör något konstruktivt i din catch-sats eller låt bli att ha den...
- MSv:Felhanterings lager?
Och ja det är inga problem att få den informationen du pratar om, problemet är data(varibler) som skickas in till funktionerna här behövs något smart vis att få med dem i Exceptionen utan att baka in dem i en sträng.Sv: Felhanterings lager?
Då missförståd jag dig!
Som jag ser det så kan man lösa det på 2 sätt.
Antingen så loggar du direkt där felet kan uppstå och loggar med all den information som du önskar. (vilket du inte ville)
Det andra blir att skapa en egen exceptionklass där du kan ha en hashtabel eller något som du kan droppa ner värden till. Sen kastar du felet vidare upp och i PL så låter den logrutinen plocka ut all information du behöver och logga det.
- MSv:Felhanterings lager?
Jag funderade först på om det fanns någon standard eller något som var förutspråkat men det är helt upp till vad man själv anser här om jag förstått det rätt. Och för min del så låter som sagt 2a förslaget mäst tilltalande just nu.