Czat, a więc hub SignalR. Kod wygląda mniej więcej tak:
namespace Mon.Chat
{
public class ChatHub : Hub
{
private readonly UserManager<ApplicationUser> userManager;
private readonly SignInManager<ApplicationUser> signInManager;
public ChatHub(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)
{
this.userManager = userManager;
this.signInManager = signInManager;
}
[Authorize]
public async Task SendChatMessage(string message)
{
var user = await userManager.GetUserAsync(Context.User);
if (await userManager.IsInRoleAsync(user, "Muted")) return;
if (message.Length == 0 || message.Length > 140) return;
await Clients.All.SendAsync("ReceiveChatMessage", Context.User.Identity.Name, message);
}
[Authorize]
public async Task Mute(string username)
{
var user = await userManager.GetUserAsync(Context.User);
if (!await userManager.IsInRoleAsync(user, "Mod")) return;
var bannedUser = await userManager.FindByNameAsync(username);
if(bannedUser == null) return;
await userManager.AddToRoleAsync(bannedUser, "Muted");
// 2 min nie 2 h dla celów testowych
var timer = new Timer () { Interval = 2 * 1000, AutoReset = false };
timer.Elapsed += async (sender, e) =>
{
var unbannedUser = await userManager.FindByNameAsync(username);
userManager.RemoveFromRoleAsync(unbannedUser, "Muted");
};
timer.Start();*/
}
}
}
Ten kod rzuca wyjątkami za każdym razem, gdy lambda w timerze próbuje użyć userManager
. A więc problem jest w tych kilku linijkach:
timer.Elapsed += async (sender, e) =>
{
var unbannedUser = await userManager.FindByNameAsync(username);
userManager.RemoveFromRoleAsync(unbannedUser, "Muted");
};
Wyjątek, który leci, to System.ObjectDisposedException: 'Cannot access a disposed object.'
I rzeczywiście: w debuggerze widzimy, że userManager
jest już disposed.
Domyślam się, że dzieje się to: Czas życia huba kończy się, gdy metoda Mute
zwraca. Ichni mechanizm dependency injection wywołuje Dispose
na wszystkich zależnościach huba natychmiast, gdy jego czas życia się kończy:
The container calls
Dispose
for theIDisposable
types it creates.
Timer jest wołany już potem, zatem leci wyjątek.
Zakładając, że pomysł z timerem jest OK - JAK mam to zrobić?! A może w ogóle pomysł z Timerem jest zły, i trzeba to jakoś inaczej zrobić?
Mam 3 pomysły, ale obawiam się, że wszystkie 3 błędne.