Witam,
mam problem z wylogowaniem użytkownika z aplikacji.
Mam utworzony słownik Dictionary<idSesji, User>, przy logowaniu jest sprawdzany czy dany użytkownik jest zalogowany, jeśli tak to dostaje idSesji ze słownika, jeśli nie to ma nową sesje. Jest odpalany timer który sprawdza czy czas sesji wygasnął. Jeśli tak to użytkownik jest ze słownika usuwany. Jednakże zdarza się że po sprawdzeniu czy dany użytkownik jest zalogowany to ze słownika pokazuje że tak, natomiast przy próbie ręcznego wylogowania tego użytkownika lub zalogowania ponownie wyświetla się komunikat "Podany klucz nie znajduje się w słowniku".
Usuwanie ze słownika Remove(idSesji)
Podany klucz nie znajduje się w słowniku
- Rejestracja: dni
- Ostatnio: dni
- Postów: 39
- Rejestracja: dni
- Ostatnio: dni
- Postów: 5227
.NET Framework? .NET Core 1? 2? 3? 5?
ASP .NET? ASP .NET Core?
To się dzieje w kontrolerze? middleware? jakimś "service"?
Słownik jest statyczny? a może z DI? a jeżeli z DI to czy jest to Singleton?
A może potrzebujesz ConcurrentDictionary?
- Rejestracja: dni
- Ostatnio: dni
- Postów: 4883
Kula magiczna mówi że problem leży w linii 123
Pokaż kod
- Rejestracja: dni
- Ostatnio: dni
- Postów: 507
To oznacza, że przy https://docs.microsoft.com/pl-pl/dotnet/api/system.collections.generic.dictionary-2.containskey?view=net-5.0 jest zwracane false i próba odczytania wartości z podanego klucza kończy się wyjątkiem. Jak nie pokażesz kodu metod odpowiedzialnych za:
- timer który sprawdza czy czas sesji wygasnął
- po sprawdzeniu czy dany użytkownik jest zalogowany
- przy próbie ręcznego wylogowania tego użytkownika lub zalogowania ponownie
to nic nie jesteśmy w stanie poradzić
- Rejestracja: dni
- Ostatnio: dni
- Postów: 39
Webservice jest na NET Framework 3.5
Jest to stary serwis pisany pod Windows Mobile 6.5. Nie ma tu rozdzielonych warstw, wszystko pisane jednym ciągu.
Nie ma sensu tego już zmieniać ponieważ przechodzimy na Androida.
Niestety są klienci dla których trzeba to jeszcze utrzymywać
Słownik
private static Dictionary<int, FntAdoConnection> userSessions = new Dictionary<int, FntAdoConnection>();
Obsługa timera
private static void onTimerEvent(object source, ElapsedEventArgs e)
{
foreach (KeyValuePair<int, FntAdoConnection> us in userSessions)
{
finishNoActivityTime = us.Value.lastActivityTime;
finishNoActivityTime = finishNoActivityTime.AddMinutes(wc.sessionTimeOut);
if (finishNoActivityTime <= DateTime.Now)
{
try
{
userSessions[SPID].Close();
userSessions.Remove(us.Key);
}
catch { }
}
}
}
Lista zalogowanych użytkowników
public int getListUser(out DataSet dataSet, out string errorMessage)
{
errorMessage = string.Empty;
dataSet = new DataSet();
try
{
DataTable dt = new DataTable();
dataSet.Tables.Add(dt);
dt.Clear();
dt.Columns.Add("SPID");
dt.Columns.Add("user");
dt.Columns.Add("userID");
dt.Columns.Add("lastActivityTime");
dt.Columns.Add("UID");
dt.Columns.Add("IP");
foreach (KeyValuePair<int, FntAdoConnection> us in userSessions)
{
DataRow _ravi = dt.NewRow();
_ravi["SPID"] = us.Value.SPID;
_ravi["user"] = us.Value.user;
_ravi["userID"] = us.Value.userID;
_ravi["lastActivityTime"] = us.Value.lastActivityTime;
_ravi["UID"] = us.Value.UID;
_ravi["IP"] = us.Value.IP;
dt.Rows.Add(_ravi);
}
return (int)answerType.ok;
}
catch (Exception OtherException)
{
errorMessage = OtherException.Message;
dataSet = new DataSet();
return (int)answerType.errorOther;
}
}
Wylogowanie użytkownika
public bool logOut(int SPID, out string errorMessage)
{
try
{
errorMessage = string.Empty;
foreach (KeyValuePair<int, FntAdoConnection> us in userSessions)
if (us.Value.SPID == SPID)
{
userSessions[SPID].Close(); // Tutaj występuje ten błąd
userSessions.Remove(SPID);
break;
}
return true;
}
catch (Exception OtherException)
{
errorMessage = OtherException.Message;
return false;
}
}
Logowanie
public bool logIn(string user, string password, string IP, string UID, out int SPID, out int userID, out string dt, out string errorMessage)
{
errorMessage = string.Empty;
dt = DateTime.Now.ToString();
SPID = -1;
userID = -1;
foreach (KeyValuePair<int, FntAdoConnection> us in userSessions)
if (us.Value.user == user)
{
SPID = us.Value.SPID;
userID = us.Value.userID;
us.Value.lastActivityTime = DateTime.Now;
}
FntAdoConnection adoConnection = new FntAdoConnection(wc);
if (adoConnection.Open(out errorMessage))
{
if (adoConnection.logIn(user, password, out errorMessage))
{
errorMessage = dt.ToString();
SPID = adoConnection.SPID = adoConnection.SPID *10000 + adoConnection.userID;
userID = adoConnection.userID;
userSessions.Add(adoConnection.SPID, adoConnection);
userSessions[SPID].lastActivityTime = DateTime.Now;
userSessions[SPID].UID = UID;
userSessions[SPID].IP = IP;
return true;
}
else return false;
}
else return false;
}
- Rejestracja: dni
- Ostatnio: dni
Jak często Timer odpala czyszczenie tego słownika?
Może być tak, że w czasie iterowania udaje się wyciągnąć dane przypisane do danego ID sesji ale w tym czasie akcja odpalona przez Timer czyści słownik i przy próbie zrobienia czegokolwiek na tym zasobie leci wyjątek.
Jeśli używacie statycznych kolekcji w wielowątkowym kontekście to proponuję zapoznać się z mechanizmami blokującymi takimi jak np prosty lock albo ReaderWriterLockSlim, które zablokują dostęp do danego zasobu na czas wykonywania operacji.
- Rejestracja: dni
- Ostatnio: dni
- Postów: 39
@var: timer odpala się co 30s. a czas sesji pomiędzy ostatnią aktywnością jest 30min
ale to nie zmienia faktu, że pomimo czy to odpalenia timera czy też ręcznego wylogowania, użytkownik nadal figuruje na liście zalogowanych i nie można się na niego zalogować.
Taki przypadek zdarza się co prawda rzadko i jak na razie w nieznany dla mnie sposób. Blokuje to prace użytkowników.