Hogyan kell az MSSQL kapcsolatot lezárni, C# kivétel esetén?

Hogyan kell az MSSQL kapcsolatot lezárni, C# kivétel esetén?
2022-11-16T01:10:50+01:00
2022-11-20T01:42:58+01:00
2022-11-20T02:00:33+01:00
Papa76
Sziasztok! Szeretnék egy kis segítséget kérni, mert "megakadt a lemez". MSSQL-be szeretnék írni, az alábbi programmal: (Ciklikusan futna)

try { using (SqlConnection scSqlConn = new SqlConnection(ConnectionString)) { scSqlConn.Open(); StringBuilder sb = new StringBuilder(); sb.Append(" INSERT INTO Tabla(PC_Neve,DatumIdo,Szint)"); sb.Append(" VALUES (@PC_Neve,@DatumIdo,@Szint)"); sAns = sb.ToString(); SqlCommand SqlComm = new SqlCommand(sAns, scSqlConn); SqlComm.Parameters.Add("@PC_Neve", SqlDbType.NVarChar).Value = msPcNév == "" ? (object)DBNull.Value : msPcNév; SqlComm.Parameters.Add("@DatumIdo", SqlDbType.DateTime2).Value = DateTime.Now; SqlComm.ExecuteNonQuery(); scSqlConn.Close(); } } catch { }
Az "ExecuteNonQuery" kivételt dob, mert nincs megadva a harmadik paraméter.
A problémám az, hogy ez esetben a "Close()" nem fog lefutni, mert átugrik a "catch"-ra, és megnyitva marad a kapcsolat. Ezért megmarad a példánya, és már megint készítettem egy memória szivárogtatót. Gondolom én...

Szeretném a kivételt kezelni, és főleg megelőzni, de biztosra elkerülni nem biztos, hogy sikerül.
Mi lenne ennek a korrekt megoldása? Esetleg csak túlagyaltam, mert a csatlakozás amúgy is bezárul?
Előre is köszi, minden infót.
Mutasd a teljes hozzászólást!
A using block egy try/finally-ra fordul, ahol a finally rész automatikusan meghívja a Dispose()-t (ami tipikusan megegyezik a Close()-zal).

Using objects that implement IDisposable

Összegezve: a kérdés szempontjából ez a kód így ahogy van, teljesen jó.
Mutasd a teljes hozzászólást!

  • finally ágról hallottál már?

    try-finally - C# Reference

    Egyébként, akkor működik a kapcsolat automatikus lezárása az using blokkból való kilépésnél, ha az kapcsolatot implementáló objektum definiálja az IDisposable interfészt, és a Dispose() metódusban ténylegesen le is zárja a kapcsolatot. Egyébként utánanéztem.. nem zárja le a kapcsolatot automatikusan, tehát sejtésednek megfelelően meg kell hívnod manuálisan a catch ágban. Kicsit át kell szervezned a kódot, using sor előtt a try után már léteznie kell a connection objektumodnak, hogy a catch ágban meghívhasd a Close()-t.
    Mutasd a teljes hozzászólást!
  • Kieg: ha a Close is dobhat excetion-t, akkor aköré is kell try/catch/finally.
    Mutasd a teljes hozzászólást!
  • A using block egy try/finally-ra fordul, ahol a finally rész automatikusan meghívja a Dispose()-t (ami tipikusan megegyezik a Close()-zal).

    Using objects that implement IDisposable

    Összegezve: a kérdés szempontjából ez a kód így ahogy van, teljesen jó.
    Mutasd a teljes hozzászólást!
  • Egyébként utánanéztem.. nem zárja le a kapcsolatot automatikusan, tehát sejtésednek megfelelően meg kell hívnod manuálisan a catch ágban.

    Tudnál erre adni egy forrást, légyszi?
    Mutasd a teljes hozzászólást!
  • SqlConnection.Close Method (System.Data.SqlClient)

    Igazad van is meg nem is, a using valóban lezárja a kapcsolatot normál lefutásnál, azonban ha kifut a using szkópjából (pl exception-t dob) akkor nem fut le a generált finally blokk és nem lesz lezárva a kapcsolat, mert nem hívódik meg a Close() és a kérdés írója pont ezért van bajban vele.

    Kiemelem a fontos részt a cikkből:

    "If the SqlConnection goes out of scope, it won't be closed. Therefore, you must explicitly close the connection by calling Close."
    Mutasd a teljes hozzászólást!
  • Folytatva az általad idézett bekezdést:

    If the SqlConnection goes out of scope, it won't be closed. Therefore, you must explicitly close the connection by calling Close or Dispose. Close and Dispose are functionally equivalent.

    A doksi többi része most nem releváns, a kérdező nem használ connection pool-t.
    Mutasd a teljes hozzászólást!
  • Igen, ez így van :) De ezt most miért is írtad? :D
    Mutasd a teljes hozzászólást!
  • A using által generált kód meghívja a Dispose()-t (ez lényegében a Close()).

    Magától valóban nem hívódik meg a Dispose(), tehát ha csak annyit írsz (using nélkül), hogy

    SqlConnection scSqlConn = new SqlConnection(ConnectionString);
    és a scSqlConn objektum kimegy a scope-ból, akkor valóban nem zárul le a kapcsolat, erre figyelmeztet az általad idézett rész.

    Én pont arról beszélek, hogy a using legenerálja egy finally-ban ezt a hívást (ld. a hivatkozott doksit).

    A using declaration-nel, pedig még scope-ot kell sem készítened, csak ennyit írsz:

    using SqlConnection scSqlConn = new SqlConnection(ConnectionString);
    és a metódus végén generálódik majd le a finally-ban a Dispose().
    Mutasd a teljes hozzászólást!
  • Történetesen a kérdező kódja használ connection pool-t, így, ahogy van, de ez irreleváns a téma szempontjából.
    Mutasd a teljes hozzászólást!
  • Azért, mert nem helyes az állításod, miszerint a Dispose() hívása a kérdező kódjában lévő using szerkezet által nem elégséges, és hogy a catch ág miatt kifuthat a scope-ból az SqlConnection.

    Nézd ezt a példát:

    using System; class Something : IDisposable { public void Dispose() { "Dispose meghívva".Dump(); } public void DoSomething() { "DoSomething meghívva, exception dobva".Dump(); throw new Exception(); } } public class Program { public static void Main() { try { using (Something s = new Something()) { s.DoSomething(); } } catch (Exception) { "Exception kezelve".Dump(); } } }
    A kimenet:

    Dumping object(String) DoSomething meghívva, exception dobva Dumping object(String) Dispose meghívva Dumping object(String) Exception kezelve
    Azért nem látja a kérdező, hogy a Close()/Dispose() meghívódik, mert megtéveszti a debugger, mivel csak a catch ágra áll rá, mint következő végrehajtandó utasítás. A valóság az, hogy a Dispose() azonnal meghívódik a using szerkezet által, még a catch ág végrehajtása előtt.

    Az igazán helyes szerkezet ez lenne:

    SqlConnection conn = null; try { conn = new SqlConnection(...); ... //nincs conn.Close() itt } catch (Exception ex) { ... } finally { conn?.Dispose(); }
    Mutasd a teljes hozzászólást!
  • Igazad van, a connection string-ben ez a paraméter by default "true".
    Mutasd a teljes hozzászólást!
  • Nem tudom, nem próbáltam ki, csak a leírás alapján ez tűnt logikusnak. Köszönöm, hogy kijavítottál :) Hiába, Entity Framework öl, butít :)

    A kódodat még átnézem majd...

    Oké, átnéztem... nem másoltam be, de persze, hogy kiírja, mert az exception dobás előtt írattad ki.. csakhogy az SqlConnection onjektum is előbb dobja a kivételt, minthogy be tudná szerencsétlen Close-olni, szóval szerintem csaltál! :)
    Mutasd a teljes hozzászólást!
  • ((Bocs, hogy csak most írok, de napközben nem tudok belépni ide.))

    Nagyon örülök, és köszönöm, hogy megint sokat tanultam tőletek. Az elmúlt négy órában (csak ezzel foglalkozom), legalább harmincszor átolvastam minden hozzászólást, és mindenkitől tanultam is. Átolvastam az összes hivatkozást, többször is.

    Elindítottam, percekig futott. kb. 40000 bejegyzést generált, az SQL Profiler-be mire megállítottam. Ez idő alatt nem érzékeltem memória szivárgást.

    Nekem most az jött át, hogy jó az "using { }"-os megoldás, amit sok példában látok is?
    De akkor lenne az igazán jó, helyes ha átszervezném a try/catch/finally szerkezetre, amelyről H.Lilla példát is készített?
    Mutasd a teljes hozzászólást!
  • DoSomething meghívva, exception dobva

    Ez az üzenet nem számít, csak azért írtam ki, hogy tudd, hol jár a kód futása. A másik kettő sorrendje az, ami számít.
    Mutasd a teljes hozzászólást!
  • Akkor jól értem, hogy ez azt bizonyítja, hogy az using az én esetemben is felszabadítja a memóriát, még akkor is, ha - ahogy írtad is - én nem látom?
    Mutasd a teljes hozzászólást!
  • Visual Studio 2022-ben
    1) a Tools->Options menüpontban keresd ki a Debug Settings alatt az "Enable Just My Code" beállítást és vedd ki előle a pipát.
    2) a Tools->Options menüpontban keresd ki a Symbol Settings-t, és jelöld be az "Always load symbols" opciót
    3) az Exception Settings ablakban vedd ki a pipát teljesen a CLR Exceptions mellől
    4) tegyél egy breakpoint-ot a using blokk kezdetéhez
    5) indítsd el a debugging-ot (nagyon lassú lesz), a breakpoint-nál megáll a végrehajtás
    6) a Debug->Windows->Modules ablakban a System.Data.SqlClient.dll assembly-re jobb klikk, "Decompile source code"
    7) állj rá a kódodban az SqlConnection-re, majd "Go to definition", és tegyél egy breakpoint-ot a Dispose() metódus elejére
    8) tegyél a te kódodban a catch ág elejére egy breakpoint-ot
    9) folytasd a debugging-ot és nézd meg, hogy a Dispose() metódusba rakott breakpoint fog először érvényesülni, és csak azután kezdődik el a catch ág végrehajtása

    Ha ez bonyolult, használd a decorator design pattern-t, hogy egy wrapper osztályt készíts a DbConnection abstract class köré, és ugyanígy tudsz a breakpoint-okkal próbálgatni. Az Open() metódust pedig úgy implementáld, hogy dobjon egy Exception-t.
    Mutasd a teljes hozzászólást!
  • Kedves kolléga,

    már itt leírtam, dokumentummal alátámasztva, a lényeget kiemelve, hogy a válasz: igen.

    Még a Close() sem kell a kódodba, a kérdés szempontjából úgy jó, ahogy van.
    Mutasd a teljes hozzászólást!
  • a using blokkból kilépve be fogja zárni a connectiont, mivel a using végén a connectionon meghívja a dispose-t. Nem kell neked explicit lezárni. Ezért is szeretjük annyira a using blokkot.
    Mutasd a teljes hozzászólást!
  • H.Lilla! Külön köszönöm a segítségedet, mert most is többet tanultam tőled, mint amire számítottam. Ezt is alkalmazni fogom a jövőben is.

    Nagyon köszönöm mindenkinek!
    Mutasd a teljes hozzászólást!
Tetszett amit olvastál? Szeretnél a jövőben is értesülni a hasonló érdekességekről?
abcd