I have this:
public class ScheduledAgent : ScheduledTaskAgent
{
...
protected override void OnInvoke(ScheduledTask task)
{
var userSettings = Utils.LoadSettings();
try
{
var client = new CheckinServiceSoapClient();
var token = WsApiCaller.Token;
var point = ... // User Location;
if (point != null)
{
client.UserTrackAsync(token, userSettings.UserId,
point.Latitude, point.Longitude,
point.Position.Location.HorizontalAccuracy,
point.Position.Location.Altitude,
point.Position.Location.Speed,
point.Position.Location.VerticalAccuracy,
point.Position.Location.Course,
"BACKGROUND_AGENT");
}
}
catch
{
}
NotifyComplete();
}
}
OnInvoke event occurs. But call of UserTrackAsync is not executing.
Your client.UserTrackAsync is an async call. The problem is that NotifyComplete(); is executed before client.UserTrackAsync has a chance to finish.
You need to call it in the UserTrackCompleted handler (and delete it from the original place):
client.UserTrackCompleted += (sender, args) =>
{
var res = args.Result.Retval;
NotifyComplete();
};
Related
I'm doing an application using server streaming.
The problem is the client doesn't read the data from the server streaming.
This is my proto service:
service UserService {
rpc GetData(Id) returns (stream DataResponse) {}
}
message Id {
int32 id = 1;
}
message DataResponse {
bytes data = 1;
}
c# server is like this:
public override async Task GetData(Id request, IServerStreamWriter<DataResponse> response, ServerCallContext context)
{
var user = {} // get user
foreach (var d in user.Data)
{
await response.WriteAsync(new DataResponse { Data = d });
}
}
And it works because I have a NodeJS client where I can call the server and works perfectly.
Client in Node is
let call = client.getData({id:1})
call.on('data', function (response) {
// do things
})
call.on('end', function () {
// do things
})
And c# client is:
AsyncServerStreamingCall<DataResponse> response = client.GetData(new Id{Id_ = 1});
while(await response.ResponseStream.MoveNext())
{
Console.WriteLine("Into while loop"); // <-- This is never executed
DataResponse current = response.ResponseStream.Current;
Console.WriteLine($"{current.Data}");
}
I've also added a try/catch and it doesn't output anything so it seems MoveNext() is always false.
What is the problem here? Why NodeJS client works and c# client can't read the stream? Have I missed something?
Here is full client.cs class:
class Program
{
const int Port = 50051;
static void Main(string[] args)
{
try
{
Channel channel = new Channel("127.0.0.1:50051", ChannelCredentials.Insecure);
var client = new UserService.UserServiceClient(channel);
GetDataStreaming(client);
}
catch (RpcException ex)
{
Console.WriteLine($"Error: {{Code: {ex.StatusCode}, Status: {ex.Status.Detail}}}");
}
}
private static async void GetDataStreaming(UserService.UserServiceClient client)
{
AsyncServerStreamingCall<DataResponse> response = client.GetData(new Id { Id_ = 1 });
while (await response.ResponseStream.MoveNext())
{
Console.WriteLine("Into while loop");
DataResponse current = response.ResponseStream.Current;
Console.WriteLine($"{current.Data.ToStringUtf8()}");
}
}
}
The issue is that your client has ended before the client receive the response. When you call GetDataStreaming(client) in Main it doesn't wait and finishes.
To fix the issue change async void GetDataStreaming to async Task GetDataStreaming.
private static async Task GetDataStreaming(UserService.UserServiceClient client)
{
AsyncServerStreamingCall<DataResponse> response = client.GetData(new Id { Id_ = 1 });
while (await response.ResponseStream.MoveNext())
{
Console.WriteLine("Into while loop");
DataResponse current = response.ResponseStream.Current;
Console.WriteLine($"{current.Data.ToStringUtf8()}");
}
}
Change static void Main to static async Task Main and you should also call channel.ShutdownAsync method at the end.
static async Task Main(string[] args)
{
try
{
Channel channel = new Channel("127.0.0.1:50051", ChannelCredentials.Insecure);
var client = new UserService.UserServiceClient(channel);
await GetDataStreaming(client);
await channel.ShutdownAsync();
}
catch (RpcException ex)
{
Console.WriteLine($"Error: {{Code: {ex.StatusCode}, Status: {ex.Status.Detail}}}");
}
}
Another option is to change async void GetDataStreaming to async Task GetDataStreaming and in Main method wait until Task complete.
static void Main(string[] args)
{
try
{
Channel channel = new Channel("127.0.0.1:50051", ChannelCredentials.Insecure);
var client = new UserService.UserServiceClient(channel);
var task = GetDataStreaming(client);
task.Wait();
}
catch (RpcException ex)
{
Console.WriteLine($"Error: {{Code: {ex.StatusCode}, Status: {ex.Status.Detail}}}");
}
}
I have a try/catch block in both my application and unit test. I'm trying to create a test that catches an exception being thrown in my actor. When debugging through the code I get the exception being thrown but in my test I never receive the exception.
Application:
public class FooActor : ReceiveActor {
private readonly IFooService fooService;
private readonly IChildActorFactory childCreatorFactory;
private IActorRef barActor;
public FooActor(IFooService fooService, IChildActorFactory childCreatorFactory) {
this.fooService = fooService;
this.childCreatorFactory = childCreatorFactory;
ReceiveAsync<bool>(async (x) => await StartAsync(x).ConfigureAwait(false));
}
protected override void PreStart() {
barActor = childCreatorFactory.Create<BarActor>(Context, "BarActor");
}
public async Task StartAsync(bool start) {
try {
if (start) {
var fooInformation = await fooService.GetInformationAsync().ConfigureAwait(false);
if (fooInformation != null) {
barActor.Tell(fooInformation);
}
}
} catch (Exception exception) {
throw new Exception($"Unhandled exception. Actor {Self.Path.Name};", exception);
}
}
}
Test:
[Fact]
public void StartAsync_ThrowsException_ExceptionThrown() {
using (var mock = AutoMock.GetLoose()) {
//Arrange
Sys.UseAutofac(mock.Container);
var mockChildActorFactory = mock.Mock<IChildActorFactory>();
var mockBarService = mock.Mock<IBarService>();
mockBarService.Setup(x => x.GetInformationAsync()).Throws(new Exception());
var props = Props.Create(() => new FooActor(mockBarService.Object, mockChildActorFactory.Object));
var fooActorName = "FooActor";
var fooActor = new TestActorRef<FooActor>(Sys, props, null, fooActorName);
try {
// Act
fooActor.Receive(true);
} catch (Exception exception) {
// Assert
Assert.Equal($"Unhandled Exception. Actor { fooActorName }.", exception.Message);
}
}
}
The problem is in the async/await operators in ReceiveAsync method:
ReceiveAsync<bool>(async (x) => await StartAsync(x).ConfigureAwait(false));
when execution context reached the await operation it just start Task at thread pool(simplified) and returns to the caller. I.e. when
fooActor.Receive(true);
completed, the actual task with StartAsync(x) may not be started yet. So when actual exception is thrown your test is already finished without any exception.
I have an api method which runs in background (async) for ~6 mins.
I want to allow the user to cancel it from another api request.
For Ex.
/api/credits/post/25 will start the background task and returns OK response immediately.
/api/credits/stop/25 will stop running background task.
my current code.
API
[Route("api/[controller]")]
public class CreditController : Controller
{
private readonly CreditService _creditService;
public CreditController (CreditService creditService)
{
_creditService = creditService;
}
[HttpPost("[action]")]
public string Post(int id)
{
if (_creditService.Instances.Any(p => p.ID == id))
return "already running " + id;
_creditService.Post(id, (s,e) =>
{
// sending realtime changes to user using SignalR.
// so that they will see progress and whats running in background
});
return "started " + id;
}
[HttpPost("[action]")]
public string Stop(int id)
{
var instance = _creditService.Instances.SingleOrDefault(p => p.ID == id));
if (instance == null)
return id + " is not running";
instance.CancelToken.Cancel();
return id + " stoppped";
}
}
Service
public class CreditService
{
public static List<CreditService> Instances = new List<CreditService>();
public Status<object> Status = new Status<object>();
public CancellationTokenSource CancelToken = new CancellationTokenSource();
public Task Post(int id, PropertyChangedEventHandler handler = null)
{
Instances.Add(this);
if (handler != null) Status.PropertyChanged += handler;
return Task.Factory.StartNew(() =>
{
try
{
// long running task
}
catch (Exception e)
{
Status.Error = e.Message ?? e.ToString();
}
finally
{
if (handler != null) Status.PropertyChanged -= handler;
Status.Clear();
CancelToken.Dispose();
Instances.Remove(this);
}
});
}
}
This is working as expected. but i want to know is this a good approach or is there any other better alternative to this.
Also this will not work if i use load balancer and multiple instances of application. How do i fix this.
Retry action Image
I am building a Xamarin iOS & android App, and I want to implement a retry function to all the failed webcall or in case of disconnection, I already use Polly in the BLL side, and I want to give the user the possibility to retry manually as shown on the above image.
protected List<Task> _taskList;
_taskList.Add(Task.Run(async () =>
{
try
{
**// Webservice Call**
Task<UtilisateurDTO> utilisateurTask = UserFactory.Login(username, pwd,
App.Hardware.GetDeviceId());
UtilisateurDTO utilisateur = await utilisateurTask;
if (utilisateur != null)
{
InvokeOnMainThread(() =>
{
**// Set result to ui component**
});
}
}
catch (Exception ex)
{
InvokeOnMainThread(() =>
{
// Add action button "Retry" to snackBar
_snackBar = new TTGSnackbar("ex.Message", TTGSnackbarDuration.Forever, "Retry", (obj) => {
// **Retry all tasks**
Parallel.ForEach(_taskList, task => task.Start());
});
_snackBar.Show();
});
}
}));
I know that it's not possible to retry completed tasks, and I can't call my web service outside a task (to not block the UI thread), so what's the alternative?
Update with a solution
If you want to handle exceptions and retry in one place, here is my solution (not the best cause it reload everything)
// BaseViewClass
public abstract class BaseViewController:UIViewController
{
// Function to override in child controllers
protected abstract void ReloadData(TTGSnackbar obj);
public void HandleExceptions(Exception e)
{
// On commence par cacher la ProgressBar
InvokeOnMainThread(HideLoadigProgressBar);
if (e is ConnectionLostException)
{
Console.WriteLine("ConnectionLostException: " + e.ToString());
InvokeOnMainThread(() =>
{
_snackBar = new TTGSnackbar("Connection lost !", TTGSnackbarDuration.Forever, "retry", ReloadData);
_snackBar.Show();
});
}
else if (e is TimeoutException)
{
Console.WriteLine("TimeoutException: "+ e.ToString());
InvokeOnMainThread(() =>
{
_snackBar = new TTGSnackbar("TimeoutException", TTGSnackbarDuration.Forever, "Retry", ReloadData);
_snackBar.Show();
});
}
.....................
}
// Other ViewController
public partial class HomeController : BaseViewController
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
LoadData();
}
public void LoadData(){
Task.Run(async () =>
{
try
{
// Web calls
Task<UtilisateurDTO> getUserTask = AccueilFactory.GetUser();
UtilisateurDTO utilisateur = await getUserTask;
// Set UI result
}
catch(Exception ex) {
HandleExceptions(ex);
}
}
}
protected override void ReloadData(TTGSnackbar obj)
{
LoadData();
}
}
You have to call your function again on exception, not only rerun last task.
Your code will be like this:
private void Login()
{
try
{
LoginInner(); // here you call service and update UI
}
catch (Exception ex)
{
InvokeOnMainThread(() =>
{
// Add action button "Retry" to snackBar
_snackBar = new TTGSnackbar("ex.Message", TTGSnackbarDuration.Forever, "Retry", (obj) => {
// **Retry all tasks**
Parallel.ForEach(_taskList, LoginInner); // ** call again loginInner **
});
_snackBar.Show();
});
}
}
I have this following code. Which is used to call a wcf service async. I cant figure out what the handler does and the args or why it is used(How can i use it). I know that the method returns a task and i use this task to await in the method it is called from. Any help will be appreciated.
public Task<List<Student>> GetStudents()
{
var tcs = new TaskCompletionSource<List<Student>>();
EventHandler<GetStudentsCompletedEventArgs> handler = null;
handler = (sender, args) =>
{
if (args.UserState == tcs)
{
service.GetStudentsCompleted -= handler;
if (args.Error != null)
{
tcs.TrySetException(args.Error);
}
else if (args.Cancelled)
{
tcs.TrySetCanceled();
}
else
{
tcs.TrySetResult(args.Result);
}
}
};
service.GetStudentsCompleted += handler;
service.GetStudentsAsync(tcs);
return tcs.Task;
}
The code converts from the Event Asynchronous Pattern(EAP)(which is exposed by the WCF client) to a task(TAP).