I have a problem. I am using the following code in my App.xaml.cs:
public static int agentId;
public static List<Agent> agents;
public App()
{
InitializeComponent();
loadingAgents += onLoadingAgents;
LoadAgentList();
MainPage = new MainPage();
}
private event EventHandler loadingAgents = delegate { };
private async void onLoadingAgents(object sender, EventArgs args)
{
Agents = await RestService.GetAgents();
}
private void LoadAgentList()
{
loadingAgents(this, EventArgs.Empty);
}
public static int AgentId
{
get
{
return agentId;
}
set
{
agentId = value;
}
}
public static List<Agent> Agents
{
get
{
if (agents == null)
{
agents = new List<Agent>();
}
return agents;
}
set
{
agents = value;
AgentId = Agents.Min(x => x.Id);
}
}
Now what this simply should do is the following:
On startup, it collects all the Agents from a webcall and puts it in a List<Agent> Agents
Once the List<Agent> Agents is set, it sets the AgentId to the minimum Id of the Agents list
The problem is that in the ViewModel of the MainPage I use those variables that are supposed to be set, but the code of the ViewModel is faster than the code that needs to update the variables.
How can I wait for the variables to finish before hitting this line: MainPage = new MainPage();?
First i don't think it is necessary to use private event EventHandler loadingAgents.Then you get Agents is an asynchronous operation,so MainPage = new MainPage(); has been executed when the method is called(at this time, the Agents may be null).And in general we don't call asynchronous tasks in the constructor to get the data,you could do it in the OnStart() method.
You could try to change like below :
public App()
{
InitializeComponent();
}
protected async override void OnStart()
{
Agents = await RestService.GetAgents();
MainPage = new MainPage();
}
Related
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:
public class TimerManager
{
private static readonly Dictionary<string, GameTimer> 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<StandardTimer>(_serviceProvider, gameState);
}
else
{
return ActivatorUtilities.CreateInstance<ChessTimer>(_serviceProvider, gameState, _gamesService);
}
}
}
I created my own base GameTimer class which encapsulates a System.Timers.Timer inside of it:
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();
}
}
Then I inherited from the abstract GameTimer class to create different types of timers.
public class StandardTimer : GameTimer
public class ChessTimer : GameTimer
The problem is inside of the ChessTimer class:
public class ChessTimer : GameTimer
{
private readonly GameState _gameState;
private readonly IGameService _gameService;
private readonly IHubContext<GameHub, IGameClient> _hubContext;
private readonly IMatchmakingService _matchmakingService;
private readonly IGamesService _gamesService;
public ChessTimer(
GameState gameState,
IGamesService gamesService,
IGameService gameService,
IHubContext<GameHub, IGameClient> hubContext,
IMatchmakingService matchmakingService)
{
_gameState = gameState;
_gameService = gameService;
_hubContext = hubContext;
_matchmakingService = matchmakingService;
_gamesService = gamesService;
Reset();
_timer.Elapsed += async (sender, args) => 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 >= 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 => x.Value <= 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();
}
}
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.
if (_gameState.IsGameOver)
{
_matchmakingService.RemoveGameState(_gameState.GameId);
await _gamesService!.SaveGameAsync(new SaveGameInputModel
{
GameId = _gameState.GameId,
Players = _gameState.Players
});
Dispose();
}
Reset();
The problem is that when the game ends and the above code is executed it produces the following exception:
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.<.ctor>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.<>c.<ThrowAsync>b__128_1(Object state)
at System.Threading.QueueUserWorkItemCallback.<>c.<.cctor>b__6_0(QueueUserWorkItemCallback quwi)
at System.Threading.ExecutionContext.RunForThreadPoolUnsafe[TState](ExecutionContext executionContext, Action`1 callback, TState& state)
at System.Threading.QueueUserWorkItemCallback.Execute()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()
at System.Threading.Thread.StartCallback()
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.
public GameTimer CreateTimer(GameState gameState)
{
if (gameState.RemainingSecondsByUserNames.Count == 0)
{
return ActivatorUtilities.CreateInstance<StandardTimer>(_serviceProvider, gameState);
}
else
{
return ActivatorUtilities.CreateInstance<ChessTimer>(_serviceProvider, gameState, _gamesService);
}
}
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.
I create all timers from a method inside my SignalR hub class:
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();
}
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
I'm working on Xamarin forms project and i have implemented Interstitial ad on all three platforms (Google admob doesn't support UWP). Ads are working fine but they are showing with a delay of 4 to 5 seconds. All i want is to finish that delay so they can show immediately.
PCL class.
public interface IAdmobInterstitial
{
void Show(string adUnit);
}
Droid Code.
public class InterstitialAdListener : AdListener
{
readonly InterstitialAd _ad;
public InterstitialAdListener(InterstitialAd ad)
{
_ad = ad;
}
public override void OnAdLoaded()
{
base.OnAdLoaded();
if (_ad.IsLoaded)
_ad.Show();
}
}
public class AdmobInterstitial : Controls.IAdmobInterstitial
{
InterstitialAd _ad;
public void Show(string adUnit)
{
var context = Android.App.Application.Context;
_ad = new InterstitialAd(context);
_ad.AdUnitId = adUnit;
var intlistener = new InterstitialAdListener(_ad);
intlistener.OnAdLoaded();
_ad.AdListener = intlistener;
var requestbuilder = new AdRequest.Builder().AddTestDevice("302E90D530B2193F59FDD7F22A11B45A");
_ad.LoadAd(requestbuilder.Build());
}
}
iOS Code.
public class AdmobInterstitial : IAdmobInterstitial
{
Interstitial _adInterstitial;
public void Show(string adUnit)
{
_adInterstitial = new Interstitial(adUnit);
var request = Request.GetDefaultRequest();
_adInterstitial.AdReceived += (sender, args) =>
{
if (_adInterstitial.IsReady)
{
var window = UIApplication.SharedApplication.KeyWindow;
var vc = window.RootViewController;
while (vc.PresentedViewController != null)
{
vc = vc.PresentedViewController;
}
_adInterstitial.PresentFromRootViewController(vc);
}
};
_adInterstitial.LoadRequest(request);
}
}
Calling interstitial ad on PCL page.
DependencyService.Get<IAdmobInterstitial>().Show("(id will come here)");
Navigation.PushAsync(new Page());
Ad is showing perfectly but with a delay. I want to show the ad first and then the page.
So i just solved this.
Here is the code.
PCL Class. Add another function to your interface.
public interface IAdmobInterstitial
{
void Show(string adUnit);
void Give();
}
Droid code. Put _ad.Show() in the new function.
public class InterstitialAdListener : AdListener
{
readonly InterstitialAd _ad;
public InterstitialAdListener(InterstitialAd ad)
{
_ad = ad;
}
public override void OnAdLoaded()
{
base.OnAdLoaded();
//if (_ad.IsLoaded)
// _ad.Show();
}
}
public class AdmobInterstitial : Controls.IAdmobInterstitial
{
InterstitialAd _ad;
public void Show(string adUnit)
{
var context = Android.App.Application.Context;
_ad = new InterstitialAd(context);
_ad.AdUnitId = adUnit;
var intlistener = new InterstitialAdListener(_ad);
intlistener.OnAdLoaded();
_ad.AdListener = intlistener;
var requestbuilder = new AdRequest.Builder().AddTestDevice("302E90D530B2193F59FDD7F22A11B45A");
_ad.LoadAd(requestbuilder.Build());
}
public void Give()
{
if (_ad.IsLoaded)
_ad.Show();
}
}
Now, call the function Show() in the constructor of your MainPage in PCL and call the function Give() when you press the button for the next page.
public MainPage()
{
InitializeComponent();
DependencyService.Get<IAdmobInterstitial>().Show("ca-app-pub-3940256099942544/1033173712");
}
private void button_Clicked(object sender, EventArgs e)
{
DependencyService.Get<IAdmobInterstitial>().Give();
Navigation.PushAsync(new Percentage());
}
This is only for Android. Same goes for iOS as well.
I hope it helps the other programmers. :)
I'd like to keep track of the number of modules that have been loaded over the total so the user can see how much loading is left. So I've decided to load each module individually and then publish an event.
I can see that the Subscriber is getting hit and running through all the correct code, but the UI wont update until all modules have been loaded. So my progress bar just goes from 0 straight to 100.
So to clarify, the problem I have is that the UI appears to be frozen throughout the entire module loading process. Is there any way around this so I can utilize a progress bar?
Bootstrapper
{
protected override void InitializeModules()
{
FillModuleCatalogFromConfig();
// begin the initialization process
eventAggregator = this.Container.Resolve<IEventAggregator>();
eventAggregator.GetEvent<BeginLoadingModulesEvent>().Publish(true);
// load the rest of the modules
InitializeRemainingModules();
eventAggregator.GetEvent<BeginLoadingModulesEvent>().Publish(false);
}
private void InitializeRemainingModules()
{
foreach (var module in ModuleCatalog.Modules)
{
InitializeModule(module);
}
}
private void InitializeModule(ModuleInfo moduleInfo)
{
if (moduleInfo.State == ModuleState.Initialized)
return;
if (moduleInfo.DependsOn.Count > 0)
{
// Load any dependencies first
foreach (var dependenciesModulesName in moduleInfo.DependsOn)
{
// if the dependency isn't loaded then we'll have to load that first
ModuleInfo module = ModuleCatalog.Modules.First(x => x.ModuleName == dependenciesModulesName);
if (module.State != ModuleState.Initialized)
{
// must initialize this module first
InitializeModule(module);
}
}
}
eventAggregator.GetEvent<MyEvent>().Publish(new ProgressChangedEventArgs(CalculateModulesLoadedProgress(), moduleInfo.ModuleName));
moduleManager.LoadModule(moduleInfo.ModuleName);
}
private int CalculateModulesLoadedProgress()
{
decimal progress = Decimal.Divide(ModuleCatalog.Modules.Where(x => x.State == ModuleState.Initialized).Count(), ModuleCatalog.Modules.Count()) * 100;
return (int)(Math.Round(progress));
}
}
ViewModel to the shell that displays the progress bar
public Class ShellViewModel
{
IEventAggregator ea;
ShellViewModel(IEventAggregator ea)
{
this.ea = ea;
this.ea.GetEvent<MyEvent>().Subscribe(this.UpdateProgressBar);
}
public int ProgressValue
{
get { return progressValue; }
set { SetProperty(ref progressValue, value); }
}
private void UpdateProgressBar(ProgressChangedEventArgs args)
{
// this all gets hit and runs fine, but the actual UI bar for progress
// wont get hit until all modules are done loading
this.ProgressValue = args.ProgressPercentage;
}
}
I did end up figuring this out. Or at least getting working the way I wanted it to. I just used the Dispatcher and set it to a background priority.
Instead of
moduleManager.LoadModule(moduleInfo.ModuleName);
I used
Application.Current.Dispatcher.Invoke(() => moduleManager.LoadModule(moduleInfo.ModuleName), DispatcherPriority.Background);
I dont think that I would use EventAggregator for this I think that I would use a regular event with a Progress window to display this something like:
public sealed class ProgressEvent
{
public EventHandler AddModule;
private static ProgressEvent _instance = new ProgressEvent();
public static ProgressEvent GetInstance()
{
return _instance;
}
public void OnAddModule(object sender, EventArgs e)
{
var addModuleDelegate = AddModule as EventHandler;
if(addModuleDelegate != null)
{
addModuleDelegate.Invoke(sender, e);
}
}
}
and inside the module:
public void Initialize()
{
//do work here
ProgressEvent.GetInstance().OnAddModule(this, new EventArgs());
}
register to it like:
public Progressbar()
{
InitializeComponent();
ProgressEvent.GetInstance().AddModule += AddProgress;
}
You defiantly need to load on separate thread.
list Task.Run(()=> load modules);
The example below functions fine, but I want to have the Complete event fire its event handlers in the UI thread. I don't want HasCompleted() to have to worry about checking if it's on the UI thread or not. Calls to HasCompleted() should always be invoked on the UI thread. How do I do this?
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var game = new Game();
game.Complete += HasCompleted;
game.Load();
}
private void HasCompleted()
{
if (label1.InvokeRequired)
{
label1.BeginInvoke(new Action(() => label1.Text = "complete"));
}
else
{
label1.Text = "complete";
}
}
}
public class Game
{
public Game()
{
}
public event MethodInvoker Complete;
public void Load()
{
var task = new Task(new Action(() =>
{
Thread.Sleep(500);
OnComplete();
}));
task.Start();
}
private void OnComplete()
{
if (Complete != null)
{
Complete();
}
}
}
Capture the current synchronization context when you create the Game object and use that to marshal the event to the context that was current when the object was first created:
public class Game
{
private SynchronizationContext context;
public Game()
{
context = SynchronizationContext.Current ??
new SynchronizationContext();
}
public MethodInvoker Complete;
public void Load()
{
//...
}
private void OnComplete()
{
if (Complete != null)
{
context.Post(_ => Complete(), null);
}
}
}
before posting the question i did my research for 10 days so really hope someone can shed some light into solving this issue.
The issue is that any bindable control, does not update once the binding list from singleton class is changed. This is a common issue on multi-threaded apps. Most if not all solutions offer suggestions where the bindlinglist or collection is initialized from parent thread, and then some invocation to be made. Not what i'm looking for. The same issue persist if static class is used instead of singleton.
Basically, the application triggers some Tasks, which in turn create object(s) on different business classes. These objects post messages into the bindinglist, which should update the UI listbox, but does not. And yes, the message object is in the list, and binding after the TASK finished works (items displayed). Locking/unlocking object(s) access is also not an issue.
Appreciate any suggestions/solutions
A trimmed down version of business objects:
namespace MyNameSpace
{
public class Message
{
private string messageSummary;
public Message() { }
public string MessageSummary
{
set { messageSummary = value; }
get { return messageSummary; }
}
}
}
A trimmed down version of another class doing some ops:
namespace MyNameSpace
{
public class WorkDoingClass
{
public WorkDoingClass() { }
public void DoSomeWork()
{
//some routines
Message messageObj = new Message();
messageObj.MessageSummary = "DoSOmrWork Finished";
}
public void DoSomeOtherWork()
{
//some routines
Message messageObj = new Message();
messageObj.MessageSummary = "DoSomeOtherWork Finished";
AllMessages.Instance.AllMessagesBindingList.Add(messageObj);
}
}
}
Singleton:
namespace MyNameSpace
{
public sealed class AllMessages
{
private static readonly AllMessages _instance = new AllMessages();
private BindingList<Message> _allMessagesBL;
public WorkDoingClass() { _allMessagesBL = new BindingList<Message>(); }
public static AllMessages Instance
{
get { return _instance; }
}
public BindingList<Message> AllMessagesBindingList
{
get { return _allMessagesBL};
}
}
}
This is also a trimmed down version from where calls start:
namespace MyNameSpace
{
public partial class Form1 : Form
{
private Task _TaskSqlData;
private CancellationTokenSource cTokenSourceSql;
public Form1()
{
InitializeComponent();
listBox1.DataSource = AllMessages.Instance.AllMessagesBindingList;
listBox1.DisplayMember = "MessageSummary";
}
private void button1_Click(object sender, EventArgs e)
{
cTokenSourceSql = new CancellationTokenSource();
var tokenSqlData = cTokenSourceSql.Token;
if (this._TaskSqlData != null)
{
if (this._TaskSqlData.Status == TaskStatus.Running)
this.cTokenSourceSql.Cancel();
this._TaskSqlData.Dispose();
this._TaskSqlData = null;
}
_TaskSqlData = Task.Factory.StartNew(()
=> StartDoingWork(this, tokenSqlData, null), tokenSqlData);
}
public void StartDoingWork(object sender, CancellationToken ct, EventArgs e)
{
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
WorkDoingClass work = new WorkDoingClass();
work.DoSomeOtherWork();
}
Your problem is that the thread(the main UI thread) making the listbox is different from the thread(the worker thread) modifying the collection.
Try the following code. It could solve your issue. I use SynchronizationContext to synchronize the two threads, which serves as the same function with Control.Invoke().
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private Task _TaskSqlData;
private CancellationTokenSource cTokenSourceSql;
WorkDoingClass _work;
public Form1()
{
InitializeComponent();
listBox1.DataSource = AllMessages.Instance.AllMessagesBindingList;
listBox1.DisplayMember = "MessageSummary";
_work = new WorkDoingClass(SynchronizationContext.Current);
}
private void button1_Click(object sender, EventArgs e)
{
cTokenSourceSql = new CancellationTokenSource();
var tokenSqlData = cTokenSourceSql.Token;
if (this._TaskSqlData != null)
{
if (this._TaskSqlData.Status == TaskStatus.Running)
this.cTokenSourceSql.Cancel();
this._TaskSqlData.Dispose();
this._TaskSqlData = null;
}
_TaskSqlData = Task.Factory.StartNew(()
=> StartDoingWork(this, tokenSqlData, null), tokenSqlData);
}
public void StartDoingWork(object sender, CancellationToken ct, EventArgs e)
{
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
_work.DoSomeOtherWork();
}
}
public class Message
{
private string messageSummary;
public Message() { }
public string MessageSummary
{
set { messageSummary = value; }
get { return messageSummary; }
}
}
public class WorkDoingClass
{
private SynchronizationContext _syncContext;
public WorkDoingClass() { }
public WorkDoingClass(SynchronizationContext _syncContext)
{
// TODO: Complete member initialization
this._syncContext = _syncContext;
}
public void DoSomeWork()
{
//some routines
Message messageObj = new Message();
messageObj.MessageSummary = "DoSOmrWork Finished";
}
public void DoSomeOtherWork()
{
_syncContext.Send(DoWork, null);
}
private static void DoWork(object arg)
{
//some routines
Message messageObj = new Message();
messageObj.MessageSummary = "DoSomeOtherWork Finished";
AllMessages.Instance.AllMessagesBindingList.Add(messageObj);
}
}
public sealed class AllMessages
{
private static readonly AllMessages _instance = new AllMessages();
private BindingList<Message> _allMessagesBL;
public AllMessages() { _allMessagesBL = new BindingList<Message>(); }
public static AllMessages Instance
{
get { return _instance; }
}
public BindingList<Message> AllMessagesBindingList
{
get { return _allMessagesBL; }
}
}
}