Leistung und Thread-Sicherheit ASP.NET Core MVCC#

Ein Treffpunkt für C#-Programmierer
Anonymous
 Leistung und Thread-Sicherheit ASP.NET Core MVC

Post by Anonymous »

Ich arbeite seit etwas mehr als zwei Jahren als .NET C#-Entwickler in einem Softwareunternehmen. Seit fast einem Jahr leite ich (als Teamleiter/PM) ein Projekt, das bereits vor meinem Beitritt begonnen hat. Ich komme ziemlich gut zurecht, aber mir fehlt etwas Erfahrung – insbesondere in Bezug auf Leistung und Thread-Sicherheit.
Das Projekt wird mit ASP.NET Core MVC unter Verwendung von ADO.NET (kein Entity Framework) und ohne Frontend-Framework (nur HTML, CSS und JavaScript) erstellt. Es handelt sich um ein Verwaltungssystem für Fitnessstudios, das für die gleichzeitige Verwendung durch viele Fitnessstudios konzipiert ist und alle eine einzelne SQL Server-Instanz (eine gemeinsame Datenbank) abfragt.
Meine größten Zweifel betreffen die Programmeinrichtung und insbesondere den DataLayer, die Klasse, die wir zur Verarbeitung von Datenbankaufrufen verwenden. Bei diesem DataLayer wird die Verbindung im Konstruktor geöffnet, und dann werden innerhalb der einzelnen Methoden wieder zusätzliche Verbindungen geöffnet (ich habe nie wirklich verstanden, warum – das war schon so, als ich ankam).
Der DataLayer selbst ist nicht als Dienst registriert; Stattdessen wird es in jedem Dienst instanziiert. Mittlerweile sind die Dienste selbst im Programm als Singletons registriert.
Hier ist ein vereinfachtes Codebeispiel:

Code: Select all

class DataLayer
{
private readonly string _connectionString;
public SqlConnection _objConnection = new SqlConnection();
public SqlCommand _objCommand = new SqlCommand();
public int _intNumRecords;
private Exception _objException;
private bool _blnTrans = false;
public SqlTransaction _objTransaction;
private string _strLastSQLExecuted;
private readonly IConfiguration _configuration;

public DataLayer(IConfiguration _configuration)
{
_connectionString = _configuration.GetConnectionString("DevConnection");

if (_connectionString is null)
_connectionString = CustumConfigurationString.GetCustumStringConfiguration("DevConnection");

try
{
_objConnection = new SqlConnection(_connectionString);
_objConnection.Open();
}
catch (Exception ex)
{
Log.WriteLog(logType: LogType.Error, LogDestination.DataBase, "", "", ex);
_objConnection.Close();
}
}

public async Task ExecuteStoreGetDataTableValueAsync(string storedProcedureName, ArrayList parameters, [CallerMemberName] string callerMember = "", [CallerFilePath] string callerFile = "", [CallerLineNumber] int callerLine = 0)
{
DataTable dt = new DataTable();

SqlConnection connection = new SqlConnection(_connectionString);
SqlCommand command = new SqlCommand(storedProcedureName, connection);

try
{
command.CommandType = CommandType.StoredProcedure;

foreach (SqlParameter parameter in parameters)
{
command.Parameters.Add(parameter);
}

await connection.OpenAsync();
using SqlDataReader reader = await command.ExecuteReaderAsync();
dt.Load(reader);

}
catch (Exception ex)
{
//Aggiungo informazioni sul punto da cui è stato chiamato il metodo per facilitare il debug
string nomeClasse = System.IO.Path.GetFileNameWithoutExtension(callerFile);
string msg = $" Chiamato da Classe: [{nomeClasse}], Metodo: [{callerMember}], Linea: [{callerLine}], SP: [{storedProcedureName}]";

await Log.WriteLogAsync(LogType.Error, LogDestination.All, msg, "", ex);

throw;
}
finally
{
if (connection is object)
{
connection.Close();
connection.Dispose();
}
if (command is object)
{
command.Parameters.Clear();
command.Dispose();
}
}

return dt;
}

}
Programm:

Code: Select all

builder.Services.AddSingleton();
builder.Services.AddSingleton();
...

Beispiel einer Dienstleistung:

Code: Select all

public class MestiereService : IMestiereService
{
private DataLayer _dataLayer;

public MestiereService(IConfiguration configuration)
{
_dataLayer = new DataLayer(configuration);
}

public async Task  GetAll(int idMestiere, string idPalestra)
{
MestieriViewModel viewModel = new MestieriViewModel();
viewModel.ListaMestieri = await GetListaMestieri(idPalestra);

if (idMestiere != 0)
{
SqlParameter idMestiereParam = new SqlParameter("@IdMestiere", idMestiere);
SqlParameter idPalestraParam = new SqlParameter("@IdPalestra", idPalestra);

string query = "sp_Get_MestiereById_Mestiere";

DataTable dt = new DataTable();
dt = await _dataLayer.ExecuteStoreGetDataTableValueAsync(query, new ArrayList() { idMestiereParam, idPalestraParam });
viewModel.Mestiere.IdMestiere = (int)idMestiere;
viewModel.Mestiere.Nome = dt.Rows[0]["Nome"].ToString();
viewModel.Mestiere.IdPalestra = Convert.ToInt32(dt.Rows[0]["IdPalestra"]);

return viewModel;
}
else
{
return viewModel;
}

}
}
Nachdem ich mich mit verschiedenen KI-Tools (ChatGPT, Cursor, Gemini) umgehört habe, scheint man sich einig zu sein, dass dieser Ansatz für den DataLayer problematisch ist – er kann zu Überlaufproblemen und Thread-Blockierungen führen. Die Empfehlung lautet, ihn in einen richtigen Dienst umzuwandeln, ihn im Programm als „Scoped“ zu registrieren und auch die anderen Dienste „Scoped“ anstelle von „Singleton“ zu machen.
Da dies eine ziemlich große Änderung wäre, die fast alles im Projekt betrifft, wollte ich von einigen erfahrenen Entwicklern hören, bevor ich mich ausschließlich auf KI-Ratschläge verlasse

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post