I have a XXXWriter. Is this the correct Fire and Forget approach in C#?
public void Add(XXX model)
{
Task.Run(() => // Fire and forget?
{
using (var ctx = new FormsEntities())
{
var dbXXX = new DALXXX();
dbXXX.Foo = model.Foo;
try
{
ctx.DALXXX.Add(dbXXX);
ctx.SaveChanges();
}
catch (Exception ex)
{
Log.Log.LogError(ex.GetMostInnerException(), "whatever");
}
}
});
}
I would recommend that the implementation is refactored as below
public class XXXWriter
{
public static void FireAndForget(XXX model)
{
Task.Run(() => DoFireAndForgetAsync(model));
}
private void DoFireAndForgetAsync(XXX model)
{
try
{
using (var ctx = new FormsEntities())
{
var dbXXX = new DALXXX();
dbXXX.Foo = model.Foo;
ctx.DALXXX.Add(dbXXX);
ctx.SaveChanges();
}
}catch (Exception ex)
{
// Remember that the Async code needs to handle its own
// exceptions, as the "DoFireAndForget" method will never fail
Log.Log.LogError(ex.GetMostInnerException(), "whatever");
}
}
}
Related
I think I understand the error but I have no idea how to solve it :/
This is the error I'm getting:
"JAR library references with identical file names but different contents were found: __reference__guava.jar. Please remove any conflicting libraries from EmbeddedJar, InputJar and AndroidJavaLibrary. Food_Recipe_App.Android"
Reason to why I want Xamarin.Google.Guava is that someone said that it would fix an issue I had earlier;
"System.NullReferenceException
Message=Object reference not set to an instance of an object."
Which, honestly, I have no idea how to solve either.
I'm thankful for all help :)
Edit: It breaks after calling the Firestore.Read();
protected override async void OnAppearing()
{
base.OnAppearing();
//using (SQLiteConnection conn = new SQLiteConnection(App.DatabaseLocation))
//{
// conn.CreateTable<Recipe>();
// var recipes = conn.Table<Recipe>().ToList();
// //recipeListView.ItemsSource = recipes;
//};
var recipes = await Firestore.Read(); ***//this line breaks***
//assignRecipesToDays(recipes);
}
This is the Read method from my Firestore script:
public async Task<List<Recipe>> Read()
{
try
{
hasReadRecipes = false;
var collection = FirebaseFirestore.Instance.Collection("recipes");
var query = collection.WhereEqualTo("userId", FirebaseAuth.Instance.CurrentUser.Uid);
query.Get().AddOnCompleteListener(this);
for (int i = 0; i < 50; i++)
{
await System.Threading.Tasks.Task.Delay(100);
if (hasReadRecipes)
break;
}
return recipes;
}
catch (Exception ex)
{
return recipes;
}
}
And this is the whole Firestore Script if that helps solving it:
using Android.App;
using Android.Content;
using Android.Gms.Tasks;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Firebase.Auth;
using Firebase.Firestore;
using Food_Recipe_App.Assets.Classes;
using Food_Recipe_App.Helpers;
using Java.Interop;
using Java.Util;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xamarin.Forms;
[assembly: Dependency(typeof(Food_Recipe_App.Droid.Dependencies.Auth))]
namespace Food_Recipe_App.Droid.Dependencies
{
public class Firestore : Java.Lang.Object, IFirestore, IOnCompleteListener
{
List<Recipe> recipes;
bool hasReadRecipes = false;
public Firestore()
{
recipes = new List<Recipe>();
}
public async Task<bool> Delete(Recipe recipe)
{
try
{
var collection = FirebaseFirestore.Instance.Collection("recipes");
collection.Document(recipe.Id).Delete();
return true;
}
catch (Exception ex)
{
return false;
}
}
public async Task<bool> Insert(Recipe recipe)
{
try
{
var recipeDocument = new Dictionary<string, Java.Lang.Object>()
{
{ "title", recipe.title },
{ "description", recipe.description },
{ "creatorUserId", FirebaseAuth.Instance.CurrentUser.Uid }
};
var collection = Firebase.Firestore.FirebaseFirestore.Instance.Collection("recipes");
collection.Add(new HashMap(recipeDocument));
return true;
}
catch (Exception ex)
{
return false;
}
}
public void OnComplete(Android.Gms.Tasks.Task task)
{
if (task.IsSuccessful)
{
var documents = (QuerySnapshot)task.Result;
recipes.Clear();
foreach (var doc in documents.Documents)
{
Recipe newRecipe = new Recipe()
{
title = doc.Get("title").ToString(),
description = doc.Get("description").ToString(),
Id = doc.Id
};
recipes.Add(newRecipe);
}
}
else
{
recipes.Clear();
}
hasReadRecipes = true;
}
public async Task<List<Recipe>> Read()
{
try
{
hasReadRecipes = false;
var collection = FirebaseFirestore.Instance.Collection("recipes");
var query = collection.WhereEqualTo("userId", FirebaseAuth.Instance.CurrentUser.Uid);
query.Get().AddOnCompleteListener(this);
for (int i = 0; i < 50; i++)
{
await System.Threading.Tasks.Task.Delay(100);
if (hasReadRecipes)
break;
}
return recipes;
}
catch (Exception ex)
{
return recipes;
}
}
public async Task<bool> Update(Recipe recipe)
{
try
{
var recipeDocument = new Dictionary<string, Java.Lang.Object>()
{
{ "title", recipe.title },
{ "description", recipe.description },
{ "creatorUserId", FirebaseAuth.Instance.CurrentUser.Uid }
};
var collection = FirebaseFirestore.Instance.Collection("recipes");
collection.Document(recipe.Id).Update(recipeDocument);
return true;
}
catch (Exception ex)
{
return false;
}
}
}
}
I am calling 2 async methods in loop.
First one is to create node and second is to add relation.
its creating node all time but its not creating all relationships
Eg - if there are 4 degrees , its creating 3 degrees but its not creating relationship with all 4. even if JobPost and JobDegrees both nodes are present.
Why its not creating relationship?
DegreesList - here I am passing array of an object.
Here is the code -
private void Map_Job_DEGREES(GUID JobId, dynamic DegreesList)
{
try
{
foreach (var objdegree in DegreesList)
{
string degreename = Convert.ToString(objdegree.name);
degreename = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(degreename);
JobDegrees objnew1 = new JobDegrees();
objnew1.name = degreename;
Task.Run(async () => await repo.Insert_JobDegrees(objnew1));
Task.Run(async () => await repo.CreateRelationship_DEGREES_INCLUDED(JobId, degreename));
}
}
catch { }
}
public class JobPostingNeo4jRepository
{
public async Task Insert_JobDegrees(JobDegrees obj1)
{
try
{
await _graphClient.ConnectAsync();
await _graphClient.Cypher
.Merge("(obj1:JobDegrees { name: $name })")
.OnCreate()
.Set("obj1 = $obj1")
.WithParams(new
{
name = obj1.name,
obj1
})
.ExecuteWithoutResultsAsync();
}
catch (Exception ex)
{
throw ex;
}
}
public async Task CreateRelationship_DEGREES_INCLUDED(Guid JobId, string name)
{
try
{
await _graphClient.ConnectAsync();
await _graphClient.Cypher
.Match("(obj1:JobPost)", "(obj2:JobDegrees)")
.Where((JobPost obj1) => obj1.Id == JobId)
.AndWhere((JobDegrees obj2) => obj2.name == name)
.Merge("(obj1)-[:DEGREES_INCLUDED { createddate: $createddate }]->(obj2)")
.WithParams(new { createddate = DateTime.UtcNow })
.ExecuteWithoutResultsAsync();
}
catch (Exception ex)
{
throw ex;
}
}
}
I want to make a Wifimanager class for UWP (Raspberry pi 3)
I looked at some tutorials and figured i was good to go.
So I came up with this:
public class WifiManager
{
private WiFiAdapter adapter;
public List<WifiNetwork> Networks;
public WifiManager()
{
Initialize();
}
private async void Initialize()
{
var access = await WiFiAdapter.RequestAccessAsync();
if (access != WiFiAccessStatus.Allowed)
{
throw new WifiAdaperAccessDeniedException();
}
else
{
var result = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(WiFiAdapter.GetDeviceSelector());
if (result.Count >= 1)
{
adapter = await WiFiAdapter.FromIdAsync(result[0].Id);
}
else
{
throw new NoWifiAdapterFoundException();
}
}
}
public async Task GetAvailableNetWorksAsync()
{
try
{
if (adapter != null)
{
await adapter.ScanAsync();
}
}
catch (Exception err)
{
throw new WifiAdaperAccessDeniedException();
}
Networks = new List<WifiNetwork>();
foreach(var network in adapter.NetworkReport.AvailableNetworks)
{
Networks.Add(new WifiNetwork(network, adapter));
}
}
}
However when I try to get the Available Networks using the async function the adapter is null. When I remove the if statement around await adapter.ScanAsync(); I get an AccessViolation.
I dont have much experience with async tasks and such, but the tutorials i found did not gave me the explaination i needed to fix this.
The problem is that you are calling an async method from the constructor. Since you do no wait for the result and are probably calling GetAvailableNetWorksAsync directly after the constructor the adapter variable has not been set yet..
You need to take it out of the constructor like this:
public class WifiManager
{
private WiFiAdapter adapter;
public List<WifiNetwork> Networks;
public async Task InitializeAsync()
{
var access = await WiFiAdapter.RequestAccessAsync();
if (access != WiFiAccessStatus.Allowed)
{
throw new WifiAdaperAccessDeniedException();
}
else
{
var result = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(WiFiAdapter.GetDeviceSelector());
if (result.Count >= 1)
{
adapter = await WiFiAdapter.FromIdAsync(result[0].Id);
}
else
{
throw new NoWifiAdapterFoundException();
}
}
}
public async Task GetAvailableNetWorksAsync()
{
try
{
if (adapter != null)
{
await adapter.ScanAsync();
}
}
catch (Exception err)
{
throw new WifiAdaperAccessDeniedException();
}
Networks = new List<WifiNetwork>();
foreach(var network in adapter.NetworkReport.AvailableNetworks)
{
Networks.Add(new WifiNetwork(network, adapter));
}
}
}
Now your calling code probably looks like this:
var wifiManager = new WifiManager();
await wifiManager.GetAvailableNetWorksAsync();
change that to
var wifiManager = new WifiManager();
await wifiManager.InitializeAsync();
await wifiManager.GetAvailableNetWorksAsync();
Take away: do not call async methods in a constructor since you cannot await completion using the await keyword and using .Wait() or .Result might give other troubles . Applies for 99% of all situations.
Another approach could be something like:
public class WifiManager
{
private WiFiAdapter adapter;
public List<WifiNetwork> Networks;
pivate async Task InitializeAsync()
{
var access = await WiFiAdapter.RequestAccessAsync();
if (access != WiFiAccessStatus.Allowed)
{
throw new WifiAdaperAccessDeniedException();
}
else
{
var result = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(WiFiAdapter.GetDeviceSelector());
if (result.Count >= 1)
{
adapter = await WiFiAdapter.FromIdAsync(result[0].Id);
}
else
{
throw new NoWifiAdapterFoundException();
}
}
}
public async Task GetAvailableNetWorksAsync()
{
try
{
if (adapter == null)
{
await InitializeAsync();
}
if (adapter != null)
{
await adapter.ScanAsync();
}
}
catch (Exception err)
{
throw new WifiAdaperAccessDeniedException();
}
Networks = new List<WifiNetwork>();
foreach(var network in adapter.NetworkReport.AvailableNetworks)
{
Networks.Add(new WifiNetwork(network, adapter));
}
}
}
I have a unit test which is running forever:
[Test]
public bool test()
{
manager.Send(10);
Thread.Sleep(1000);
manager.Messages.Should().HaveCount(10);
return true;
}
manager.Send() method is:
private void Send()
{
try
{
var entities = GetAllEntities();
foreach (var group in entities.GroupBy(r => r.Priority))
{
var tasks = group.Select(entity => Task.Factory.StartNew(() => manager.SendEntity(entity))).ToList();
Task.WaitAll(tasks.ToArray());
if (tasks.All(r => r.Result.Result == true))
{
// some code here...
}
}
}
catch (Exception e)
{
logger.FatalException(e.Message, e);
}
finally
{
logger.Info("End...");
}
}
and SendEntity() method:
public Task<bool> SendEntity(DeferredEntity entity)
{
var taskCompletionSource = new TaskCompletionSource<bool>();
try
{
logger.Info("Sending entity {0} with params: {1}", entity.Entity, GetEntityParams(entity));
server.SendToServer(entity, (response, result) =>
{
taskCompletionSource.SetResult(result);
});
}
catch (Exception e)
{
logger.FatalException(e.Message, e);
}
return taskCompletionSource.Task;
}
in unit test manager.Send(10) is running forever. I debuged the code and I see that the problem is in
if (tasks.All(r => r.Result.Result == true))
the debugger stops on this line and sleeps forever. What I am doing wrong? I added return value of unit test method to bool (async methods doesn't throw exceptions in void methods). But it doesn't help. Do you have any suggestions?
You get the deadlock there.
First of all you don't have to start the new Thread with
Task.Factory.StartNew(() => manager.SendEntity(entity)
It seems SendToServer is already async.
It also a bad practice to use the Task.Wait***/Task.Result, use the async flow
private async Task Send()
{
try
{
var entities = GetAllEntities();
foreach (var group in entities.GroupBy(r => r.Priority))
{
var tasks = group
.Select(entity => manager.SendEntity(entity))
.ToArray();
var results = await Task.WhenAll(tasks);
if (results.All(result => result))
{
// some code here...
}
}
}
catch (Exception e)
{
logger.FatalException(e.Message, e);
}
finally
{
logger.Info("End...");
}
}
But if you don't want to rewrite the Send Method you can use .ConfigureAwait(false)
return taskCompletionSource.Task.ConfigureAwait(false);
But anyway, remove the StartNew - you don't need this.
I am trying to read the records from table even when the table is locked due to particular transaction.
I am using below code
public async Task<KeyValuePair<String, List<Om_Category>>> CategoryList(Om_Category obj)
{
try
{
using (var transaction = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions
{
IsolationLevel = IsolationLevel.ReadUncommitted
},
TransactionScopeAsyncFlowOption.Enabled))
{
using (var categoryContext = new ModelGeneration())
{
categoryContext.Configuration.ProxyCreationEnabled = false;
var data = await categoryContext
.tblCategory
.ToListAsync();
transaction.Complete();
return new KeyValuePair<String, List<Om_Category>>("", data);
}
}
}
catch (Exception ex)
{
return new KeyValuePair<String, List<Om_Category>>(ex.Message, null);
}
}
But seems like I am missing something to implement NoLocks. Still it
show timeout. Am I missing something ?
Surprisingly Entity Framework inbuilt Transaction Class worked !!
public async Task<KeyValuePair<String, List<BE_Category>>> CategoryList(BE_Category obj)
{
try
{
using (var categoryContext = new ModelGeneration())
{
using (var dbContextTransaction = categoryContext
.Database
.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted))
{
categoryContext.Configuration.ProxyCreationEnabled = false;
//Code
dbContextTransaction.Commit();
return new KeyValuePair<String, List<BE_Category>>("", data);
}
}
}
catch (Exception ex)
{
return new KeyValuePair<String, List<BE_Category>>(ex.Message, null);
}
}
Reference