When I try to do this it crashes:
I want to get the user data async, if I don't use async task it returns null
public class Database : MonoBehaviour
{
private DatabaseReference m_database;
private const string DATA_URL = "hidden";
public static Database singleton;
void Awake ()
{
FirebaseApp.DefaultInstance.SetEditorDatabaseUrl(DATA_URL);
m_database = FirebaseDatabase.DefaultInstance.RootReference;
DontDestroyOnLoad(this);
singleton = this;
}
void Start ()
{
User user = new User();
user = GetUserAsync("213asdasd").Result;
Debug.Log(user.email);
}
public void RegisterNewUser (User user)
{
string jsonData = JsonUtility.ToJson(user);
m_database.Child("Users").Child(user.id).SetRawJsonValueAsync(jsonData);
m_database.Child("Users").Child(user.id).Child("id").SetValueAsync(user.id);
m_database.Child("Users").Child(user.id).Child("email").SetValueAsync(user.email);
}
public async Task<User> GetUserAsync (string id)
{
User user = new User();
await FirebaseDatabase.DefaultInstance.GetReference("Users").Child(id)
.GetValueAsync().ContinueWith(task =>
{
if (task.IsFaulted)
{
// Handle the error...
}
else if (task.IsCompleted)
{
DataSnapshot snapshot = task.Result;
string rawUserData = snapshot.GetRawJsonValue();
Debug.Log(rawUserData);
user = JsonUtility.FromJson<User>(rawUserData);
}
});
return user;
}
}
Mixing async-await and blocking calls can cause problems.
Reference Async/Await - Best Practices in Asynchronous Programming
Use an async event handler if the code is unable to be refactored to be async all the way
void Start () {
started += onStarted;
started(this, EventArgs.Empty);
}
event EventHandler started = delegate { }
private async void onStarted(object sender, EventArgs args) {
started -= onStarted;
User user = await GetUserAsync("213asdasd");
Debug.Log(user.email);
}
Also code should follow async all the way pattern
public async Task<User> GetUserAsync (string id) {
User user = new User();
try {
DataSnapshot snapshot = await FirebaseDatabase.DefaultInstance
.GetReference("Users").Child(id).GetValueAsync();
string rawUserData = snapshot.GetRawJsonValue();
Debug.Log(rawUserData);
user = JsonUtility.FromJson<User>(rawUserData);
} catch(Exception ex) {
// Handle the error...
}
return user;
}
Related
Main Issue:
Unsure of what's happening in the background, but apparently whenever the line await Task.Delay(1000); is called Unity would turn unresponsive/freezes up.
Error msg:
Tried checking callbacks from unity's profiler, but whenever the line in question were triggered unity would instantly turn unresponsive and not allowing any interaction (no callbacks/error messages were given out either).
Goal:
The goal here is to simply call tryActivities[0] then, await for 1 second and then return true, and prints "Fished successfully!", without any crashing.
Suspicions:
A suspicion I have is that the line tryActivities.Add(new Func<bool>(() => fs(currentSlot.basicData.typeID).Result)); need some sort of await? Though I'm also unsure of how to implement that either.
Inventory inventory; (Inventory.cs)
List<Func<bool>> tryActivities = new List<Func<bool>>();
public delegate Task<bool> Fish(int ID); Fish fs;
void Start()
{
fs = new Fish(activities.Fish);
tryActivities.Add(new Func<bool>(() => fs(currentSlot.basicData.typeID).Result));//await? how?
}
public void Interact()//called when a button is pressed
{
if (TryPlaceOrUse()) print("Fished successfully!");
else print("Fishing failed");
}
bool TryPlaceOrUse()
{
if (tryActivities[(int)currentSlot.basicData.myActivity]())//tryActivities[0]
return true;
return false;
}
Activities activities; (Activities.cs)
public async Task<bool> Fish(int ID)
{
await Task.Delay(1000);//crashes here
return true;
}
This might do what you want. This is how you'd use a list of async Funcs, which is really just a list of Funcs that return Tasks of bool.
public class Program
{
public delegate Task<bool> Fish(int ID);
Fish fs;
List<Func<Task<bool>>> tryActivities = new List<Func<Task<bool>>>();
public async static Task Main()
{
Console.WriteLine("Hello, World!");
Program program = new Program();
program.Start();
await program.Interact();
}
void Start()
{
fs = new Fish(Activities.Fish);
tryActivities.Add(new Func<Task<bool>>(async () => await fs(4)));//await? how? like this :)
}
public async Task Interact() //called when a button is pressed
{
if (await TryPlaceOrUse())
Console.WriteLine("Fished successfully!");
else
Console.WriteLine("Fishing failed");
}
async Task<bool> TryPlaceOrUse()
{
if (await tryActivities[0]()) //tryActivities[0]
return true;
return false;
}
}
public class Activities
{
public static async Task<bool> Fish(int ID)
{
await Task.Delay(1000);//does not crash here
return true;
}
}
I have the following SignalR Hub with in-memory connection management.
[Authorize]
public class ChatHub : Hub
{
private static readonly ConnectionMapping<string> _connections =
new ConnectionMapping<string>();
private readonly IMessageRepository _messagesRepository;
public ChatHub(IMessageRepository messageRepository)
{
_messagesRepository = messagesRepository;
}
public override async Task OnConnectedAsync()
{
var name = Context.User.Identity.Name;
_connections.Add(name, Context.ConnectionId);
//await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception exception)
{
var name = Context.User.Identity.Name;
_connections.Remove(name, Context.ConnectionId);
//await Groups.RemoveFromGroupAsync(Context.ConnectionId, "SignalR Users");
await base.OnDisconnectedAsync(exception);
}
public async void SendMessage(ChatMessage chatMessage)
{
var name = Context.User.Identity.Name;
var chatHistoryMessage = await _messagesRepository.SaveMessageAsync(chatMessage);
var availableConnections = _connections.GetConnections(name);
if (availableConnections.Any())
{
foreach (var connectionId in availableConnections)
{
Clients.Client(connectionId).SendAsync("ReceiveMessage", chatHistoryMessage);
}
}
else
{
}
}
}
However, when executing the code the following line
Clients.Client(connectionId).SendAsync("ReceiveMessage", 1);
raises an object disposed error on Clients.
This issue started happening when I added the repository line:
var chatHistoryMessage = await _messagesRepository.SaveMessageAsync(chatMessage);
SaveMessageAsync method:
public async Task<ChatHistory> SaveMessageAsync (ChatMessage chatMessage)
{
using (var conn = new SqlConnection(ConnProvider.ConnectionString))
{
await conn.OpenAsync();
return (await conn.QueryAsync<ChatHistory>("[mob].[spSaveChatMessage]",
new
{
..
},
commandType: CommandType.StoredProcedure)).FirstOrDefault();
}
}
Why would my Clients object be disposed? If I wait with the debugger that issue never happens.
It looks like the SendMessage method should be an async Task rather than an async void.
The issue could be caused by the way the SignalR framework runs async voids.
See this article for a good overview.
Async methods returning void don’t provide an easy way to notify the
calling code that they’ve completed.
I have a problem, and I am not quite sure how to solve this, except for making my Akka Actor not have async methods.
Here is my Actor Code:
public class AggregatorActor : ActorBase, IWithUnboundedStash
{
public IStash Stash { get; set; }
private AggregatorTimer _aggregatorTimer;
private IActorSystemSettings _settings;
private AccountSummary _accountResponse;
private ContactDetails _contactResponse;
private AnalyticDetails _analyticsResponse;
private FinancialDetails _financialResponse;
private ActorSelection _accountActor;
private ActorSelection _contactActor;
private ActorSelection _analyticsActor;
private ActorSelection _financialActor;
public AggregatorActor(IActorSystemSettings settings) : base(settings)
{
_accountActor = Context.System.ActorSelection(ActorPaths.AccountActorPath);
_contactActor = Context.System.ActorSelection(ActorPaths.ContactActorPath);
_analyticsActor = Context.System.ActorSelection(ActorPaths.AnalyticsActorPath);
_financialActor = Context.System.ActorSelection(ActorPaths.FinancialActorPath);
_settings = settings;
}
#region Public Methods
public override void Listening()
{
ReceiveAsync<ProfilerMessages.ProfilerBase>(async x => await HandleMessageAsync(x));
}
private void Busy()
{
Receive<ProfilerMessages.ProfilerBase>(x => Stash.Stash());
}
private void Aggregate()
{
try
{
Context.Sender.Tell(AggregatedSummaryResponse.Instance(_accountResponse, _contactResponse, _analyticsResponse, _financialResponse));
}
catch (Exception ex)
{
ExceptionHandler(ex);
}
}
public override async Task HandleMessageAsync(object msg)
{
//if is summary, generate new isntance of AggregatorTimer in _eventHandlerCollection.
if (msg is ProfilerMessages.GetSummary)
{
//Become busy. Stash
Become(Busy);
//Handle different requests
var clientId = (msg as ProfilerMessages.GetSummary).ClientId;
await HandleSummaryRequest(clientId);
}
}
private async Task HandleSummaryRequest(string clientId)
{
try
{
var accountMsg = new AccountMessages.GetAggregatedData(clientId);
_accountResponse = (await _accountActor.Ask<Messages.AccountMessages.AccountResponseAll>(accountMsg, TimeSpan.FromSeconds(_settings.NumberOfSecondsToWaitForResponse))).AccountDetails;
//Need to uncomment this
var contactMsg = new ContactMessages.GetAggregatedContactDetails(clientId);
_contactResponse = (await _contactActor.Ask<Messages.ContactMessages.ContactResponse>(contactMsg, TimeSpan.FromSeconds(_settings.NumberOfSecondsToWaitForResponse))).ContactDetails;
var analyticMsg = new AnalyticsMessages.GetAggregatedAnalytics(clientId);
_analyticsResponse = (await _analyticsActor.Ask<Messages.AnalyticsMessages.AnalyticsResponse>(analyticMsg, TimeSpan.FromSeconds(_settings.NumberOfSecondsToWaitForResponse))).AnalyticDetails;
var financialMsg = new FinancialMessages.GetAggregatedFinancialDetails(clientId);
_financialResponse = (await _financialActor.Ask<Messages.FinancialMessages.FinancialResponse>(financialMsg, TimeSpan.FromSeconds(_settings.NumberOfSecondsToWaitForResponse))).FinancialDetails;
//Start new timer
_aggregatorTimer = new AggregatorTimer(_settings.NumberOfSecondsToWaitForResponse);
_aggregatorTimer.TimeElapsed += _aggregatorTimer_TimeElapsed;
}
catch (Exception ex)
{
ExceptionHandler(ex);
}
}
//Event that is raised when an external timers time elapsed.
private async void _aggregatorTimer_TimeElapsed(object sender, ElapsedTimeHandlerArg e)
{
Aggregate();
_aggregatorTimer = null;
_accountResponse = null;
_contactResponse = null;
_analyticsResponse = null;
_financialResponse = null;
//Unstash
Stash.Unstash();
//Start listening again
Become(Listening);
}
#endregion
}
Inside the _aggregatorTimer_TimeElapsed event, I call the await Aggregate function, but the following exception is thrown.
There is no active ActorContext, this is most likely due to use of async operations from within this actor
I think this is caused by the fact that the Aggregate function tries to Tell() the Sender about the responses that are aggregated, but those Tasksare not yet completed? I might be completely wrong, but I have no idea why this is thrown.
I'm not sure what do you even need an AggregatorTimer for - in akka you have a Context.System.Scheduler object, which can be used to schedule events going to happen in the future.
Another thing is that you probably shouldn't execute logic inside event handlers. If you really need them in your code, it's better to limit them only to send a message, once an event gets triggered i.e.:
Receive<TimedOut>(_ => /* handle timeout message */);
var self = Self; // bind current self to a variable, so it won't change
_aggregatorTimer.TimeElapsed += (sender, e) => self.Tell(new TimedOut(), self);
I have these functions
public async Task<List<Machine>> GetMachines()
{
await Initialize();
await SyncMachines();
return await machineTable.ToListAsync();
}
public async Task InsertMachines(List<Machine> machines)
{
await Initialize();
await Task.WhenAll(machines.Select(m => machineTable.InsertAsync(m)));
await SyncMachines();
}
I am writing a parent class to put these functions in, such that the functions getMachines() and InsertMachines() become getObject and InsertObject where the List<Machine> can be a list of any objects, so they can return and accept any type of list
How do I declare functions like this?
According to your description, I created my azure sync table class as follows, you could refer to it:
public class AzureCloudSyncTable<TModel> where TModel : class
{
public static MobileServiceClient MobileService = new MobileServiceClient(
"https://{your-mobile-app-name}.azurewebsites.net"
);
private IMobileServiceSyncTable<TModel> syncTable = MobileService.GetSyncTable<TModel>();
#region Offline sync
private async Task InitLocalStoreAsync()
{
if (!MobileService.SyncContext.IsInitialized)
{
var store = new MobileServiceSQLiteStore("localstore.db");
store.DefineTable<TModel>();
await MobileService.SyncContext.InitializeAsync(store);
}
await SyncAsync();
}
private async Task SyncAsync()
{
await PushAsync();
await syncTable.PullAsync(typeof(TModel).Name, syncTable.CreateQuery());
}
private async Task PushAsync()
{
try
{
await MobileService.SyncContext.PushAsync();
}
catch (MobileServicePushFailedException ex)
{
if (ex.PushResult != null)
{
foreach (var error in ex.PushResult.Errors)
{
await ResolveConflictAsync(error);
}
}
}
}
private async Task ResolveConflictAsync(MobileServiceTableOperationError error)
{
//var serverItem = error.Result.ToObject<TModel>();
//var localItem = error.Item.ToObject<TModel>();
//// Note that you need to implement the public override Equals(TModel item)
//// method in the Model for this to work
//if (serverItem.Equals(localItem))
//{
// // Items are the same, so ignore the conflict
// await error.CancelAndDiscardItemAsync();
// return;
//}
//// Client Always Wins
//localItem.Version = serverItem.Version;
//await error.UpdateOperationAsync(JObject.FromObject(localItem));
// Server Always Wins
//await error.CancelAndDiscardItemAsync();
}
#endregion
#region public methods
public async Task<List<TModel>> GetAll()
{
await InitLocalStoreAsync();
await SyncAsync();
return await syncTable.ToListAsync();
}
public async Task Insert(List<TModel> items)
{
await InitLocalStoreAsync();
await Task.WhenAll(items.Select(item => syncTable.InsertAsync(item)));
await PushAsync();
}
#endregion
}
Additionally, for more details about Handling Conflict Resolution, you could refer to adrian hall's book here.
This is a followup question to the following question:
Volatile IEnlistmentNotification and TransactionScope.AsyncFlowEnabled = true
The approach accepted in the question above works as long as you don't await multiple statements. Let me show an example:
public class SendResourceManager : IEnlistmentNotification
{
private readonly Action onCommit;
public SendResourceManager(Action onCommit)
{
this.onCommit = onCommit;
}
public void Prepare(PreparingEnlistment preparingEnlistment)
{
preparingEnlistment.Prepared();
}
public void Commit(Enlistment enlistment)
{
Debug.WriteLine("Committing");
this.onCommit();
Debug.WriteLine("Committed");
enlistment.Done();
}
public void Rollback(Enlistment enlistment)
{
enlistment.Done();
}
public void InDoubt(Enlistment enlistment)
{
enlistment.Done();
}
}
public class AsyncTransactionalMessageSender : ISendMessagesAsync
{
private readonly List<Message> sentMessages = new List<Message>();
public IReadOnlyCollection<Message> SentMessages
{
get { return new ReadOnlyCollection<Message>(this.sentMessages); }
}
public async Task SendAsync(Message message)
{
if (Transaction.Current != null)
{
await Transaction.Current.EnlistVolatileAsync(
new SendResourceManager(async () => await this.SendInternal(message)),
EnlistmentOptions.None);
}
else
{
await this.SendInternal(message);
}
}
private async Task SendInternal(Message message)
{
Debug.WriteLine("Sending");
await Task.Delay(1000);
this.sentMessages.Add(message);
Debug.WriteLine("Sent");
}
}
[Test]
public async Task ScopeRollbackAsync_DoesntSend()
{
var sender = new AsyncTransactionalMessageSender();
using (var tx = new System.Transactions.TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
await sender.SendAsync(new Message("First"));
await sender.SendAsync(new Message("Second"));
await sender.SendAsync(new Message("Last"));
// We do not commit the scope
}
sender.SentMessages.Should().BeEmpty();
}
[Test]
public async Task ScopeCompleteAsync_Sends()
{
var sender = new AsyncTransactionalMessageSender();
using (var tx = new System.Transactions.TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
await sender.SendAsync(new Message("First"));
await sender.SendAsync(new Message("Second"));
await sender.SendAsync(new Message("Last"));
tx.Complete();
}
sender.SentMessages.Should().HaveCount(3)
.And.Contain(m => m.Value == "First")
.And.Contain(m => m.Value == "Second")
.And.Contain(m => m.Value == "Last");
}
As soon as you introduce a Task.Delay like shown in the example above the generated asynchronous statemachine will never come back and invoke the this.sentMessages.Add(message) and Debug.WriteLine("Sent")
The problem is I currently see now way to properly enlist asynchronous code inside the enlistment notification. Any ideas how to tackle this challenge?