I want to write some Unittests with NUnit for our wpf application.
The application downloads some data with System.Net.WebClient in the background using the observer pattern.
Here is an example:
Download.cs
public class Download : IObservable<string>
{
private string url { get; }
private List<IObserver<string>> observers = new List<IObserver<string>>();
private bool closed = false;
private string data = null;
public Download(string url)
{
this.url = url;
startDownload();
}
public IDisposable Subscribe(IObserver<string> observer)
{
if (!observers.Contains(observer))
{
if (!closed)
{
observers.Add(observer);
}
else
{
sendAndComplete(observer);
}
}
return new Unsubscriber(observer, observers);
}
private void startDownload()
{
WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler((object sender, DownloadStringCompletedEventArgs e) => {
if (e.Error != null)
{
data = e.Result;
}
closed = true;
sendAndComplete();
});
client.DownloadStringAsync(new Uri(url));
}
private void sendAndComplete()
{
foreach (var observer in observers)
{
sendAndComplete(observer);
}
observers.Clear();
}
private void sendAndComplete(IObserver<string> observer)
{
if (data != null)
{
observer.OnNext(data);
}
else
{
observer.OnError(new Exception("Download failed!"));
}
observer.OnCompleted();
}
private class Unsubscriber : IDisposable
{
private IObserver<string> _observer { get; }
private List<IObserver<string>> _observers { get; }
public Unsubscriber(IObserver<string> _observer, List<IObserver<string>> _observers)
{
this._observer = _observer;
this._observers = _observers;
}
public void Dispose()
{
if (_observer != null && _observers.Contains(_observer))
{
_observers.Remove(_observer);
}
}
}
}
DownloadInspector.cs
public class DownloadInspector : IObserver<string>
{
private Action<string> onSuccessAction { get; }
private Action<Exception> onErrorAction { get; }
private Action onCompleteAction { get; }
public DownloadInspector(Action<string> onSuccessAction, Action<Exception> onErrorAction, Action onCompleteAction)
{
this.onSuccessAction = onSuccessAction;
this.onErrorAction = onErrorAction;
this.onCompleteAction = onCompleteAction;
}
public void OnCompleted()
{
onCompleteAction.Invoke();
}
public void OnError(Exception error)
{
onErrorAction.Invoke(error);
}
public void OnNext(string value)
{
onSuccessAction.Invoke(value);
}
}
example (usage)
Download download = new Download("http://stackoverflow.com");
DownloadInspector inspector = new DownloadInspector(
(string data) =>
{
Debug.WriteLine("HANDLE DATA");
},
(Exception error) =>
{
Debug.WriteLine("HANDLE ERROR");
},
() =>
{
Debug.WriteLine("HANDLE COMPLETE");
}
);
I'm still new in c# and not very familiar with asynchronous programming in that language. I know the await and async keywords and know that they work with NUnit, but the current construct don't use this keywords.
Can you help me creating a unit test for this case? Its okay to change/remove the observer pattern.
The constructor for the Download class starts the download, which means that I can't subscribe an observer until after the download has started. That's a race condition. It's possible (although unlikely) that observers will be notified before they can be subscribed.
public Download(string url)
{
this.url = url;
startDownload();
}
But I can go ahead and test because I'm subscribing an observer before that can happen. If you can I'd recommend not doing that. Allow the caller to construct the class in one step and then start the download with a method call.
I also had to change this method. I figured that testing for an error would be the easiest first step, but it needs to do data = e.Result if there is no error, not if there is an error.
private void StartDownload()
{
WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler((object sender, DownloadStringCompletedEventArgs e) =>
{
if (e.Error == null) // <== because of this
{
data = e.Result;
}
closed = true;
sendAndComplete();
});
client.DownloadStringAsync(new Uri(url));
}
What I didn't see coming is that WebClient.DownloadStringAsync isn't actually async. It doesn't return a Task. It just takes a callback. What that means is that there's no sure way to know whether it's done except to wait for it to notify the observer that the download is complete.
My NUnit test runner wasn't running, so I used MsTest. It's the same thing.
The basic approach is that I'm creating some flags, and the inspector responds to notifications by setting the flags. That way I can see which notifications were raised.
The last problem is that because DownloadStringComplete is a callback, the test exits before the Assert. That means it will always pass. So in order to fix it I had to do something I've never seen before, which I found here:
[TestMethod]
public void download_raises_error_notification()
{
var success = false;
bool error = false;
bool complete = false;
var pause = new ManualResetEvent(false);
var download = new Download("http://NoSuchUrlAnywhere.com");
var inspector = new DownloadInspector(
onSuccessAction: s => success = true,
onCompleteAction: () =>
{
complete = true;
pause.Set();
},
onErrorAction: s => error = true
);
download.Subscribe(inspector);
// allow 500ms for the download to fail. This is a race condition.
pause.WaitOne(500);
Assert.IsTrue(error,"onErrorAction was not called.");
}
This is technically an integration test since it must actually attempt a download in order to run. That could be remedied by mocking WebClient.
Related
I'm trying to create a UWP service app on the Raspberry Pi3 which provides the access to the on board UART. I'm facing an issue about the AppConnection Request/response.
this is the service method that handles the incoming requests from client apps
internal class Inbound
{
public static async void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
{
var messageDeferral = args.GetDeferral();
var response = new ValueSet();
bool success = false;
var msg = args.Request.Message.Keys;
if (args.Request.Message.TryGetValue(ServiceApiRequests.Keys.Command, out object command))
{
try
{
switch (command)
{
case ServiceApiRequests.CommandValues.UartWrite:
if (args.Request.Message.TryGetValue(ServiceApiRequests.Keys.UartTxBuffer, out object txBuffer))
{
string rxBuff = "";
success = await Pi3.Peripherals.Uart.GerInstance(57600).Write((string)txBuffer);
if (success)
{
Debug.WriteLine("Tx: " + (string)txBuffer);
if (args.Request.Message.TryGetValue(ServiceApiRequests.Keys.ReadUartResponse, out object getResponse))
{
if ((string)getResponse == ServiceApiRequests.ReadUartResponse.Yes)
{
rxBuff = await Pi3.Peripherals.Uart.GerInstance(57600).Read();
Debug.WriteLine("Rx: " + rxBuff);
}
}
}
response.Add(ServiceApiRequests.Keys.UartRxBuffer, rxBuff);
}
break;
}
}
catch (Exception ex)
{
success = false;
}
}
response.Add(new KeyValuePair<string, object>(ServiceApiRequests.Keys.Result, success ? ServiceApiRequests.ResultValues.Ok : ServiceApiRequests.ResultValues.Ko));
var result = await args.Request.SendResponseAsync(response);
if (result == AppServiceResponseStatus.Failure)
{
Debug.WriteLine("Failed to send the response");
}
messageDeferral.Complete();
}
}
As you can figure out, the Uart class is get using the Singleton pattern using the method Pi3.Peripherals.Uart.GerInstance(57600).
Following the code i using for send the request from the client app.
public static class Uart
{
public static IAsyncOperation<string> SendCommand(this AppServiceConnection DriverControllerConnection, string txBuffer, string awaitResponse = ServiceApiRequests.ReadUartResponse.Yes)
{
return _SendCommand(DriverControllerConnection, txBuffer, awaitResponse).AsAsyncOperation();
}
private static async Task<string> _SendCommand(AppServiceConnection DriverControllerConnection, string txBuffer, string awaitResponse)
{
AppServiceResponse response = null;
string response_str = "";
try
{
if (DriverControllerConnection != null)
{
response = await DriverControllerConnection.SendMessageAsync(new ServiceApiRequests.UartWrite().GetCommand(txBuffer, awaitResponse));
if (response.Status == AppServiceResponseStatus.Success)
{
if (response.Message.TryGetValue(ServiceApiRequests.Keys.Result, out object result))
{
if ((string)result == ServiceApiRequests.ResultValues.Ok && awaitResponse == ServiceApiRequests.ReadUartResponse.Yes)
{
response_str = response.Message[ServiceApiRequests.Keys.UartRxBuffer] as string;
}
}
}
}
}
catch (Exception ex)
{
// TODO: log
}
return response_str;
}
}
The system works well just for a while, until i have response.Status == AppServiceResponseStatus.Success , then the result of the request changes and it becomes AppServiceResponseStatus.Failure. This way the program counter never steps into the condition if (response.Status == AppServiceResponseStatus.Success).
Any idea about the cause?
Thank you so much for the help.
EDIT
Follow the suggestions, i added an handler for the ServiceClosed event. This is the main class.
public sealed class DriverListener : IBackgroundTask
{
private BackgroundTaskDeferral backgroundTaskDeferral;
private AppServiceConnection appServiceConnection;
public void Run(IBackgroundTaskInstance taskInstance)
{
backgroundTaskDeferral = taskInstance.GetDeferral();
// taskInstance.Canceled += OnTaskCanceled;
var triggerDetails = taskInstance.TriggerDetails as AppServiceTriggerDetails;
appServiceConnection = triggerDetails.AppServiceConnection;
appServiceConnection.RequestReceived += Inbound.OnRequestReceived;
appServiceConnection.ServiceClosed += OnTaskCanceled;
}
private void OnTaskCanceled(AppServiceConnection sender, AppServiceClosedEventArgs reason)
{
if (this.backgroundTaskDeferral != null)
{
Debug.WriteLine("ServiceClosed");
// Complete the service deferral.
this.backgroundTaskDeferral.Complete();
}
}
}
Placing a breakpoint in this function, i see that it was never triggered.
The app connection is opened using the singleton pattern, and putted in a dll that i use in the client app
public static AppServiceConnection GetDriverConnectionInstance()
{
if (_DriverConnectionInstance == null)
{
try
{
_DriverConnectionInstance = OpenDriverConnection().AsTask().GetAwaiter().GetResult();
}
catch
{
}
}
return _DriverConnectionInstance;
}
I also add a Request to the service that toggles a led, and i noticed that the led status changes but the response from the app service is still "Failure" and the message is null.
The AppService has a default lifetime of 25sec, unless it is being requested by the foreground experience. When the service shuts down the connection, your client process will receive the ServiceClosed event, so you know you will need to reopen the connection the next time you want to send a request.
I wrote a static class which tests the internet connectivity of the user, and raises an event if their connection status changes:
class InternetConnectionMonitor
{
public static EventHandler<EventArgs<bool>> StatusChanged;
private static bool isCancelled;
private static bool isConnected;
private static bool IsConnected
{
get
{
return isConnected;
}
set
{
if (isConnected != value)
{
StatusChanged(null, new EventArgs<bool>(value));
}
isConnected = value;
}
}
public static async void Start(TimeSpan interval)
{
//TODO Use a 1st party webpage for connectivity testing.
using (var client = new HttpClient())
{
client.Timeout = TimeSpan.FromSeconds(2);
while (!isCancelled)
{
try
{
await client.GetAsync("http://example.com");
IsConnected = true;
}
catch (Exception)
{
IsConnected = false;
}
await Task.Delay(interval);
}
}
}
public static void Stop()
{
isCancelled = true;
}
}
This class works wonderfully, however, when doing some other processing using the TPL Dataflow in my app, an exception is raised in the while loop of the Start() method saying the task was cancelled. The reason I am posting here, is because I never cancel any tasks.
Here is the processing I am doing. The task cancelled exception is raised in InternetConnectionMonitor after QueueTests is completed, although QueueTests makes no reference to InternetConnectionMonitor whatsoever.
If I don't call validateProxies(), the exception is never raised.
private async void validateProxies(IEnumerable<Proxy> proxies)
{
validateProxiesButton.Enabled = false;
cancelValidatingProxiesButton.Enabled = true;
addProxiesButton.Enabled = false;
removeProxiesButton.Enabled = false;
proxyTester = new ProxyTester();
await proxyTester.QueueTests(proxies, judges);
validateProxiesButton.Enabled = true;
cancelValidatingProxiesButton.Enabled = false;
addProxiesButton.Enabled = true;
removeProxiesButton.Enabled = true;
MessageBox.Show("Complete!");
}
public class ProxyTester
{
private PauseOrCancelTokenSource pcts = new PauseOrCancelTokenSource();
public async Task QueueTests(IEnumerable<Proxy> proxies, IEnumerable<ProxyJudge> judges, int maxConcurrency = 100)
{
var testProxies = new ActionBlock<(Proxy proxy, IProxyTest test)>((tup) =>
{
tup.proxy.Status = ProxyStatus.Testing;
tup.proxy.Status = tup.proxy.TestValidity(tup.test);
}, new ExecutionDataflowBlockOptions { CancellationToken = pcts.Token.CancellationToken, MaxDegreeOfParallelism = maxConcurrency });
//Set each proxies status to Queued, and post to the dataflow block.
foreach (var proxy in proxies)
{
proxy.Status = ProxyStatus.Queued;
await testProxies.SendAsync((proxy, judges.GetRandomItem()));
}
testProxies.Complete();
try
{
await testProxies.Completion;
}
catch (Exception)
{
}
}
public void Cancel()
{
pcts.Cancel();
}
}
Starting InternetConnectionMonitor (requested by JleruOHeP in comments)
public proxyTesterView()
{
InitializeComponent();
InternetConnectionMonitor.StatusChanged += InternetConnectionMonitor_StatusChanged;
InternetConnectionMonitor.Start(TimeSpan.FromSeconds(1));
}
private void InternetConnectionMonitor_StatusChanged(object sender, EventArgs<bool> e)
{
if (e.Value == true)
{
MessageBox.Show("Online");
}
else
{
MessageBox.Show("Offline");
}
}
Solved my own question and wanted to share my solution. After some thinking, I had a feeling perhaps threadpool threads were becoming exhausted when calling QueueTests, due to the high default max degree of parallelism (100). This threadpool exhaustion seemed to have unintended side effects on the call to client.GetAsync in the Start() method in InternetConnectionMonitor, causing the timeout on the request to improperly trigger, thus causing the TaskCancelledException.
So I spawned my own explicit thread and am synchronously performing the testing within it. Exception gone and working as intended now.
public static void Start()
{
//TODO Use a 1st party webpage for connectivity testing.
var t = new Thread(() =>
{
while (!isCancelled)
{
try
{
var req = HttpWebRequest.Create("http://example.com");
req.Timeout = 1000;
using (var resp = req.GetResponse())
{
resp.Close();
}
IsConnected = true;
}
catch (Exception ex)
{
IsConnected = false;
}
Console.WriteLine(IsConnected);
Thread.Sleep(1000);
}
});
t.Start();
}
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'm facing difficulties understanding how to handle program control during asynchronous flow.
I have a SessionManager class which calls the initiates the session and we need to register
for the event OnStartApplicationSessionResponse and my control will return to the calling point. I will get the session id in the eventhandler after sometime or the error code if there is an error.
class SessionManager
{
public bool startUp(Object params)
{
try
{
serviceProvider = new ServiceProvider();
serviceProvider.OnStartApplicationSessionResponse += new StartApplicationSessionResponseHandler(ServiceProvider_OnStartApplicationSessionResponse);
serviceProvider.startUp(params);
}
}
public void ServiceProvider_OnStartApplicationSessionResponse(object sender, ServiceProvider.StartApplicationSessionResponseArgs e)
{
//e.getError
//I will get the session Id here or error code
}
}
How do I get sessionId or the error as my control is now at the calling position?
You could use TaskCompletionSource to make the Event awaitable.
class SessionManager
{
private ServiceProvider _serviceProvider;
public int SessionId
{
get;
private set;
}
public Task<bool> StartUp(Object param)
{
_serviceProvider = new ServiceProvider();
var tcs = new TaskCompletionSource<bool>();
_serviceProvider.OnStartApplicationSessionResponse += (sender, args) =>
{
// do your stuff
// e.g.
SessionId = 0xB00B5;
tcs.SetResult(true);
};
_serviceProvider.startUp(param);
return tcs.Task;
}
}
The call would look like:
private static async void SomeButtonClick()
{
var mgr = new SessionManager();
var success = await mgr.StartUp("string");
if (success)
{
Console.WriteLine(mgr.SessionId);
// update ui or whatever
}
}
note: This Feature is available in .Net 4.5.
With the C# feature async and await you are able to rewrite an asynchronous flow into something that is like a synchronous flow. You have only provided some fragments of your code so to provide a complete example I have created some code that resembles your code:
class StartEventArgs : EventArgs {
public StartEventArgs(Int32 sessionId, Int32 errorCode) {
SessionId = sessionId;
ErrorCode = errorCode;
}
public Int32 SessionId { get; private set; }
public Int32 ErrorCode { get; private set; }
}
delegate void StartEventHandler(Object sender, StartEventArgs e);
class ServiceProvider {
public event StartEventHandler Start;
public void Startup(Boolean succeed) {
Thread.Sleep(TimeSpan.FromSeconds(1));
if (succeed)
OnStart(new StartEventArgs(321, 0));
else
OnStart(new StartEventArgs(0, 123));
}
protected void OnStart(StartEventArgs e) {
var handler = Start;
if (handler != null)
handler(this, e);
}
}
The ServiceProvider.Startup method will delay for a second before firing an event that either signals success or failure depending on the succeed parameter provided. The method is rather silly but hopefully is similar to the behavior of your ServiceProvider.Startup method.
You can convert the asynchronous startup into a task using a TaskCompletionSource:
Task<Int32> PerformStartup(ServiceProvider serviceProvider, Boolean succeed) {
var taskCompletionSource = new TaskCompletionSource<Int32>();
serviceProvider.Start += (sender, e) => {
if (e.ErrorCode > 0)
throw new Exception(e.ErrorCode.ToString());
taskCompletionSource.SetResult(e.SessionId);
};
serviceProvider.Startup(succeed);
return taskCompletionSource.Task;
}
Notice how an error signaled by the Start event is converted into an Exception (in production code you should use a custom exception type instead).
Using the async and await feature of C# you can now write code that looks very much like synchronous code even though it actually is asynchronous:
async void Startup(Boolean succeed) {
var serviceProvider = new ServiceProvider();
try {
var sessionId = await PerformStartup(serviceProvider, succeed);
Console.WriteLine(sessionId);
}
catch (Exception ex) {
Console.WriteLine(ex);
}
}
If an error is reported by the Start event you can now deal with in the catch block. Also the session ID is simply a return value of the function. The "magic" is that using await on a Task will return the result of the task when it completes and if an exception is thrown in the task it can be caught on the thread awaiting the task.
I have an async service
The service contract defined like:
[ServiceBehavior(InstanceContextMode = InstanceContext.PerCall]
Myservice
My client is defined like:
MyServiceClient task= null;
InstanceContext instanceContext = new InstanceContext(this);
task = new MyServiceClient(instanceContext);
task.MyMethod();
And the client class implements the call back methods (finish, progress etc...).
It's works fine, but if I call to the method, and she start running on the server and I shut down the server,I can't know the status of my call, and the client still think that the methods still running.
So, how can I check if this call is still running?
Thanks for helpers :)
Edit:
CallBack Interface:
public interface IServiceCallback
{
[OperationContract(IsOneWay=true)]
void NotifyFinished();
[OperationContract(IsOneWay=true)]
void NotifyProgress(int x);
[OperationContract(IsOneWay=true)]
void NotifyFailed(Exception exception);
}
Service Interface:
[ServiceContract(CallbackContract = typeof (IServiceCallback)]
public interface IAsyncService
{
[OperationContract(IsOneWay=true)]
void AsyncRunning();
}
Service Class:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class AsyncService : IAsyncService
{
private IServiceCallback ServiceCallback {get; set;}
public void AsyncRunningProxy ()
{
for(int x=0; x<100 ; x++)
{
AsyncService.NotifyProgress(x);
}
}
private void EndMethod(IAsyncResult res)
{
AsyncResult result = (AsyncResult)res;
try
{
((dynamic)result.AsyncDelegate).EndInvoke(res);
AsyncService.NotifyFinished();
}
catch (Exception e)
{
AsyncService.NotifyFailed(e);
}
}
public void AsyncRunning ()
{
ServiceCallback = OperationContext.Current.GetCallBackChannel<IServiceCallback>();
Action action = AsyncRunningProxy;
action.BeginInvoke(EndMethod, null);
}
}
Client Class:
public class ServiceRunner : IServiceCallback
{
private ManualResetEvent reset {get; set;}
public ServiceRunner()
{
reset = new ManualResetEvent(false);
}
public void Run()
{
AsyncServiceClient client = null;
InstanceContext instanceContext = new InstanceContext(this);
client = new AsyncServiceClient(instanceContext);
client.AsyncRunning();
reset.WaitOne();
}
public void NotifyProgress(int x)
{
Console.WriteLine(x);
}
public void NotifyFinished()
{
}
public void NotifyFailed(Exception e)
{
Console.WriteLine(e.Message);
reset.Set();
}
}
Edit: new client Class:
Client Class:
public class ServiceRunner : IServiceCallback
{
private ManualResetEvent reset { get; set; }
private string IsRunning { get; set; }
public ServiceRunner()
{
reset = new ManualResetEvent(false);
IsRunning = true;
}
public void Run()
{
AsyncServiceClient client = null;
InstanceContext instanceContext = new InstanceContext(this);
client = new AsyncServiceClient(instanceContext);
client.AsyncRunning();
new Thread(()=>
{
while(IsRunning)
{
try
{
client.IsAlive();
Thrad.Sleep(60 * 1000);
}
catch (Exception e) // The server is not responding.
{
NotifyFailed(e);
return;
}
}
}).Start();
reset.WaitOne();
}
public void NotifyProgress(int x)
{
Console.WriteLine(x);
}
public void NotifyFinished()
{
IsRunning = false;
reset.Set();
}
public void NotifyFailed(Exception e)
{
IsRunning = false;
Console.WriteLine(e.Message);
reset.Set();
}
}
In order to have more control of your clients request to the service, you should be able to use the inbuilt Task and Async support to monitor and if necessary handle connection delays.
The support for generating Task-based operations on the client side will be available to users who rely on proxies generated by our client generation tools (svcutil.exe or Add Service Reference), as well as to users who prefer to directly use ChannelFactory
The following code provides a rough example:
Task<string> task = new MyServiceClient().MyMethod();
if (task == await Task.WhenAny(task, Task.Delay(1000)))
{
Console.WriteLine(await task);
}
else
{
// handle delay …
}
Refer to the following MSDN blog entry for more information:
http://blogs.msdn.com/b/endpoint/archive/2010/11/13/simplified-asynchronous-programming-model-in-wcf-with-async-await.aspx
Regards,
As #adkSerenity mention you may implement timeout logic, but I guess your question not about that.
The callback method will be(and should be) called in case of exception for example connection lose or internal connection time out.
private static void CallbackSample(IAsyncResult asynchronousResult)
{
try
{
// State of request is asynchronous.
RequestState myRequestState=(RequestState) asynchronousResult.AsyncState;
HttpWebRequest myHttpWebRequest2=myRequestState.request;
myRequestState.response = (HttpWebResponse);
//next line may throw exception
myHttpWebRequest2.EndGetResponse(asynchronousResult);
}
catch(WebException e)
{
}
}
So async communication looks like fire and forget. Your callback method will be called when you get the result(exception too), but if you decide not handle it(custom time out logic) you must "foret" about callback processing. No way to check is alive(except of course custom api).