title: Is it possible to inject services inside a Timer in ASP.NET Core?
body: <p>I am currently developing a multiplayer web game with timer using ASP.NET Core. For real time communication I am using SignalR. Everythning up to the moment had been working just fine.
  The way I implemented the timer functionality was the following: I created a static dictionary where the key is the Id of the game and the value is the timer corresponding to the given game. Then I exposed public methods for managing the state of the dictionary:</p>
  <pre><code>public class TimerManager
  {
      private static readonly Dictionary&lt;string, GameTimer&gt; gameTimersByGameIds = new();
  
      public void AttachTimerToGameState(GameTimer timer, string gameId)
      {
          if (!gameTimersByGameIds.ContainsKey(gameId))
          {
              gameTimersByGameIds.Add(gameId, timer);
              return;
          }
  
          gameTimersByGameIds[gameId] = timer;
      }
  
      public GameTimer? GetTimer(string gameId)
      {
          if (!gameTimersByGameIds.ContainsKey(gameId))
          {
              return null;
          }
  
          return gameTimersByGameIds[gameId];
      }
  
      public GameTimer CreateTimer(GameState gameState)
      {
          if (gameState.RemainingSecondsByUserNames.Count == 0)
          {
              return ActivatorUtilities.CreateInstance&lt;StandardTimer&gt;(_serviceProvider, gameState);
          }
          else
          {
              return ActivatorUtilities.CreateInstance&lt;ChessTimer&gt;(_serviceProvider, gameState, _gamesService);
          }
      }
  }
  </code></pre>
  <p>I created my own base GameTimer class which encapsulates a System.Timers.Timer inside of it:</p>
  <pre><code>public abstract class GameTimer
  {
      protected readonly System.Timers.Timer _timer = new();
  
      public virtual void Reset()
      {
          _timer.AutoReset = true;
          _timer.Interval = 1_000;
      }
  
      public virtual void Start()
      {
          _timer.Start();
      }
  
      public virtual void Dispose()
      {
          _timer.Dispose();
      }
  }
  </code></pre>
  <p>Then I inherited from the abstract GameTimer class to create different types of timers.</p>
  <pre><code>public class StandardTimer : GameTimer
  public class ChessTimer : GameTimer
  </code></pre>
  <p>The problem is inside of the ChessTimer class:</p>
  <pre><code>public class ChessTimer : GameTimer
  {
      private readonly GameState _gameState;
      private readonly IGameService _gameService;
      private readonly IHubContext&lt;GameHub, IGameClient&gt; _hubContext;
      private readonly IMatchmakingService _matchmakingService;
      private readonly IGamesService _gamesService;
  
      public ChessTimer(
          GameState gameState,
          IGamesService gamesService,
          IGameService gameService,
          IHubContext&lt;GameHub, IGameClient&gt; hubContext,
          IMatchmakingService matchmakingService)
      {
          _gameState = gameState;
          _gameService = gameService;
          _hubContext = hubContext;
          _matchmakingService = matchmakingService;
          _gamesService = gamesService;
          Reset();
  
          _timer.Elapsed += async (sender, args) =&gt; await OnTimedEvent(sender, args);
      }
  
      public int SecondsRemaining { get; private set; }
  
      public override void Reset()
      {
          string currentPlayerName = _gameState.CurrentTeam.CurrentPlayer.UserName;
          SecondsRemaining = _gameState.RemainingSecondsByUserNames[currentPlayerName];
          base.Reset();
      }
  
      private async Task OnTimedEvent(object? sender, ElapsedEventArgs args)
      {
          if (SecondsRemaining &gt;= 0)
          {
              string currentPlayerUserName = _gameState.CurrentTeam.CurrentPlayer.UserName;
              _gameState.RemainingSecondsByUserNames[currentPlayerUserName] = SecondsRemaining;
  
              int minutes = SecondsRemaining / 60;
              int seconds = SecondsRemaining % 60;
  
              var viewModel = new UpdateGameTimerViewModel
              {
                  Minutes = minutes,
                  Seconds = seconds,
              };
  
              foreach (Player player in _gameState.Players)
              {
                  if (player.ConnectionId == null)
                  {
                      continue;
                  }
  
                  await _hubContext.Clients
                      .Client(player.ConnectionId)
                      .UpdateGameTimer(viewModel);
              }
  
              SecondsRemaining--;
              return;
          }
  
          if (_gameState.RemainingSecondsByUserNames.All(x =&gt; x.Value &lt;= 0))
          {
              _gameState.EndGame();
          }
          else
          {
              _gameState.NextTeam();
          }
  
          foreach (Player player in _gameState.Players)
          {
              _gameService.FillPlayerTiles(_gameState, player);
          }
  
          foreach (Player player in _gameState.Players)
          {
              if (player.ConnectionId == null)
              {
                  continue;
              }
  
              var viewModel = _gameService.MapFromGameState(_gameState, player.UserName);
  
              await _hubContext.Clients
                  .Client(player.ConnectionId)
                  .UpdateGameState(viewModel);
  
              if (_gameState.IsGameOver)
              {
                  _matchmakingService.RemoveUserFromGame(player.UserName);
  
                  await _hubContext.Groups
                      .RemoveFromGroupAsync(
                          player.ConnectionId, _gameState.GameId);
              }
          }
  
          if (_gameState.IsGameOver)
          {
              _matchmakingService.RemoveGameState(_gameState.GameId);
  
              await _gamesService!.SaveGameAsync(new SaveGameInputModel
              {
                  GameId = _gameState.GameId,
                  Players = _gameState.Players
              });
  
              Dispose();
          }
  
          Reset();
      }
  }
  </code></pre>
  <p>Basically the way my OnTimedEvent method works is that it executes every second. Then checks whether the time of all players has run out. If it hasn't it updates the state of the game, otherwise is ends the game. After everything else is done, I try to save the game inside my database in order to enable the users to see summary about their played games. I am using a class called GamesService for this task. It uses UserManager internally to perform some work with the Users in the database.</p>
  <pre><code>if (_gameState.IsGameOver)
  {
      _matchmakingService.RemoveGameState(_gameState.GameId);
  
      await _gamesService!.SaveGameAsync(new SaveGameInputModel
      {
          GameId = _gameState.GameId,
          Players = _gameState.Players
      });
  
      Dispose();
  }
  
  Reset();
  </code></pre>
  <p>The problem is that when the game ends and the above code is executed it produces the following exception:</p>
  <pre><code>Unhandled exception. Unhandled exception. System.ObjectDisposedException: Cannot access a disposed object.
  Object name: 'UserManager`1'.
     at Microsoft.AspNetCore.Identity.UserManager`1.ThrowIfDisposed()
     at Microsoft.AspNetCore.Identity.UserManager`1.FindByNameAsync(String userName)
     at SuperScrabble.Services.Data.Users.UsersService.GetByUserNameAsync(String userName) in C:\Users\georg\Source\Github\SuperScrabble\src\Server\Services\SuperScrabble.Services.Data\Users\UsersService.cs:line 64
     at SuperScrabble.Services.Data.Games.GamesService.SaveGameAsync(SaveGameInputModel input) in C:\Users\georg\Source\Github\SuperScrabble\src\Server\Services\SuperScrabble.Services.Data\Games\GamesService.cs:line 52
     at SuperScrabble.WebApi.Timers.ChessTimer.OnTimedEvent(Object sender, ElapsedEventArgs args) in C:\Users\georg\Source\Github\SuperScrabble\src\Server\WebApi\SuperScrabble.WebApi\Timers\ChessTimer.cs:line 120
     at SuperScrabble.WebApi.Timers.ChessTimer.&lt;.ctor&gt;b__5_0(Object sender, ElapsedEventArgs args) in C:\Users\georg\Source\Github\SuperScrabble\src\Server\WebApi\SuperScrabble.WebApi\Timers\ChessTimer.cs:line 35
     at System.Threading.Tasks.Task.&lt;&gt;c.&lt;ThrowAsync&gt;b__128_1(Object state)
     at System.Threading.QueueUserWorkItemCallback.&lt;&gt;c.&lt;.cctor&gt;b__6_0(QueueUserWorkItemCallback quwi)
     at System.Threading.ExecutionContext.RunForThreadPoolUnsafe[TState](ExecutionContext executionContext, Action`1 callback, TState&amp; state)
     at System.Threading.QueueUserWorkItemCallback.Execute()
     at System.Threading.ThreadPoolWorkQueue.Dispatch()
     at System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()
     at System.Threading.Thread.StartCallback()
  </code></pre>
  <p>It seems to me that the _gamesService object which I pass to the CreateInstance() method inside the TimerManager.CreateTimer() method is already disposed or that some of the services it uses internally are.</p>
  <pre><code>public GameTimer CreateTimer(GameState gameState)
  {
      if (gameState.RemainingSecondsByUserNames.Count == 0)
      {
          return ActivatorUtilities.CreateInstance&lt;StandardTimer&gt;(_serviceProvider, gameState);
      }
      else
      {
          return ActivatorUtilities.CreateInstance&lt;ChessTimer&gt;(_serviceProvider, gameState, _gamesService);
      }
  }
  </code></pre>
  <p>I am not sure if what I am trying to do is correct. I need to use service classes inside my timer classes to perform operations every time the timer ticks. However, I cannot use dependency injection and that's the reason why I use the ActivatorUtilities class to instantiate the object.</p>
  <p>I create all timers from a method inside my SignalR hub class:</p>
  <pre><code>private async Task StartGameAsync()
  {
      var gameState = _matchmakingService.GetGameState(UserName);
      string gameId = gameState.GameId;
  
      foreach (Player player in gameState.Players)
      {
          await Groups.AddToGroupAsync(player.ConnectionId!, gameId);
      }
  
      var timer = _timerManager.CreateTimer(gameState);
      Console.WriteLine(timer.GetType().Name);
      _timerManager.AttachTimerToGameState(timer, gameId);
  
      await Clients.Group(gameId).StartGame(gameId);
      await UpdateGameStateAsync(gameState);
  
      timer.Start();
  }
  </code></pre>
  <p>So my question would be: Is it possible to find a work around of the problem?
  If you need any more clarification please feel free to ask me anything. Any help would be greatly appreciated! Thanks</p>