IDbConnection jako singleton - jedno połączenie z bazą danych na całą aplikację (WinForms)

0

Witam.
Borykam się z problemem nadmiernego łączenia się do bazy. Kiedyś nawet zwrócono mi tutaj uwagę, że z każdym zapytaniem tworze nowe połączenie do bazy. Jak sobie z tym radzicie jak czasem macie po kilka zapytań w jednej operacji? Czy singleton jest dobrym rozwiązaniem? Czy lepiej wszędzie gdzie połączenie jest potrzebne robić

using(IDbConnection db = new SqlConnection()) { }
1

Jak to sql server to nie ma większego problemu. Jak by to był Oracle to wtedy robi się pule połączeń. Możesz poszukać jakiś biblioteki co robi takie pule połączeń i z niej skorzystać. Jak masz stałe połączenie to musisz sprawdzać jego stan czy się nie rozpięło, czy wszystko działa itp.

0

To dlaczego ktoś mi zwrócił na to uwagę w tym wątku?

0

Musisz jego zapytac :P

0

Chodziło chyba żeby wykorzystywać jedno połączenie na całą pętlę i żeby ogarnąć całość transakcją :) Pomijając Task.Result bez await.

0

@AdamWox: Weź przykładową aplikację z internetu dla .Net cora i zobacz jak tam to robią i po sprawie.

2
AdamWox napisał(a):

Jak sobie z tym radzicie jak czasem macie po kilka zapytań w jednej operacji?

Jeśli mam kilka zapytań w jednej operacji biznesowej, to mam jedno połączenie i raczej jedną transakcję. Nie wyobrażam sobie tego dzielić, to by raczej nie działało zbyt dobrze.

0

Nie mówimy tutaj o ASP, mówimy o WinForms. Niby są biblioteki do DI ale to jest masakra. Teoretycznie są 4 opcje jakie mi przyszły do głowy:

OPCJA 1 - Singleton

OPCJA 2 - chyba najgorsza. Własna klasa, która z każdym zapytaniem otwiera połączenie.

public static async Task<IEnumerable<T>> Query<T>(string query)
{
     using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString))
     {
          return await db.QueryAsync<T>(query);
     }
}

OPCJA 3 - otwieranie using tam gdzie połączenie jest potrzebne

using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString))
{
     await db.QueryAsync<TraElem>("select * from CDN.TraElem where TrE_TrNId = @ID", new { ID = id });
     await db.QueryAsync<int>(QueriesService.DocumentQuery, new { AttributeCode = "EAN", Number = documentNumber });
}

OPCJA 4 - otworzyć połączenie w Program.cs i przesłać na formę, która będzie to połączenie przesyłać na kolejne formy.

[STAThread]
static void Main()
{
     Application.EnableVisualStyles();
     Application.SetCompatibleTextRenderingDefault(false);

     BonusSkins.Register();
     SkinManager.EnableFormSkins();
     UserLookAndFeel.Default.SetSkinStyle("DevExpress Style");
     Application.Run(new frmMain(new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString)));
}
0

No najgorsza opcja to opcja 4. Połączenie z bazą danych powinno mieć krótki okres życia. JEDNAK jeśli chodzi o aplikacje desktopowe i jeśli używasz zaawansowanego ORMa (EF, nHibernate), to wtedy jest zalecenie, żeby połączenie było otwarte przez cały okres życia formy. Raczej chodzi tutaj o okienka dialogowe. Wtedy ORMy działają fajnie, bo ogarniają automatycznie update tylko zmienionych pól. Teraz można by się pokusić - czy forma główna też tak powinna pracować? Moim zdaniem nie. Bo ona życie przez całą aplikację, a nie przez chwilę. Więc połączenie jest otwarte przez całą aplikację.

Jeśli chodzi o DI, to przynajmniej Microsoft jest jeszcze bardziej radykalny i twierdzi, że połączenie z bazą danych powinno istnieć tylko dla konkretnej klasy. Mówiąc o radykalności, mam na myśli tutaj web. Microsoft radzi, żeby połączenie z bazą danych nie było otwierane dla całego requestu, tylko dla konkretnych serwisów - oddzielnie. Pewnie nie w każdej sytuacji to się sprawdzi, ale tak powinno być.

Kończąc - trzymaj otwarte połączenie tak krótko, jak się da. Generalnie @somekind dał idealne wskazówki. Wszystkie zapytania w ramach jednej operacji biznesowej powinny używać jednego połączenia. To znaczy (pseudokody):

var connection = OpenConnection();

connection.BeginTransaction();
foreach(var item in list)
{
  UpdateOrInsert(item);
}

connection.CommitTransaction();
connection.Close();

albo:

void RegisterClient(Client client)
{
  var connection = OpenConnection();

  bool result = ClientExists(connection, client);
  if(!result)
  {
     connection.BeginTransaction();
     InsertSomeClientData(connection, client);
     InsertOtherClientData(connection, client);
     UpdateSomeShit(connection);
     connection.CommitTransaction();
  }

 connection.Close();
}

(do purystów - celowo pominąłem ewentualny RollbackTransaction, żeby nie zaciemniać obrazu :))

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0