I have a project where I am using System.Net.Http.HttpClient. I am trying to centralize all calls to my Web APIs so that I have common error handing etc. I created the following class in my project.
using ModernHttpClient;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace WebAPIHelper
{
class WebAPICaller
{
public async Task<string> CallWebService(string ps_URI)
{
HttpClient lobj_HTTPClient = null;
HttpResponseMessage lobj_HTTPResponse = null;
string ls_Response = "";
//We assume the internet is available.
try
{
//Get the Days of the Week
lobj_HTTPClient = new HttpClient(new NativeMessageHandler());
lobj_HTTPClient.BaseAddress = new Uri(App.APIPrefix);
lobj_HTTPClient.DefaultRequestHeaders.Accept.Clear();
lobj_HTTPClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
lobj_HTTPResponse = await lobj_HTTPClient.GetAsync(ps_URI);
if (!lobj_HTTPResponse.IsSuccessStatusCode)
{
Debug.WriteLine(lobj_HTTPResponse.ReasonPhrase);
}
else
{
ls_Response = await lobj_HTTPResponse.Content.ReadAsStringAsync();
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
finally
{
if (lobj_HTTPClient != null)
lobj_HTTPClient.Dispose();
if (lobj_HTTPResponse != null)
{
lobj_HTTPResponse.Dispose();
}
}
return ls_Response;
}
}
}
I call the function from an instance object I created in my ViewModel class for languages as follows:
using ModernHttpClient;
using Newtonsoft.Json;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace WebAPIHelper
{
public class VM_Languages : INotifyPropertyChanged
{
/// <summary>
/// A collection for CGSLanguage objects.
/// </summary>
public ObservableCollection<GBSLanguage_ForList> Items_ForList { get; private set; }
const string ic_LanguagesAPIUrl = #"/languages/true";
/// <summary>
/// Constructor for the Languages view model.
/// </summary>
public VM_Languages()
{
this.Items_ForList = new ObservableCollection<GBSLanguage_ForList>();
}
/// <summary>
/// Indicates of the view model data has been loaded
/// </summary>
public bool IsDataLoaded
{
get;
private set;
}
/// <summary>
/// Creates and adds a the countries to the collection.
/// </summary>
public async Task LoadData()
{
HttpClient lobj_HTTPClient = null;
HttpResponseMessage lobj_HTTPResponse = null;
string ls_Response = "";
try
{
IsDataLoaded = false;
string ls_WorkLanguageURI = ic_LanguagesAPIUrl;
//Get the Days of the Week
lobj_HTTPClient = new HttpClient(new NativeMessageHandler());
lobj_HTTPClient.BaseAddress = new Uri(App.APIPrefix);
lobj_HTTPClient.DefaultRequestHeaders.Accept.Clear();
lobj_HTTPClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
**//This call will not work
//WebAPICaller lobj_APICaller = new WebAPICaller();
//ls_Response = lobj_APICaller.CallWebService(ls_WorkLanguageURI).Result;
//This call will work
lobj_HTTPResponse = await lobj_HTTPClient.GetAsync(ls_WorkLanguageURI);**
if (lobj_HTTPResponse.IsSuccessStatusCode)
{
if (this.Items_ForList != null)
this.Items_ForList.Clear();
ls_Response = await lobj_HTTPResponse.Content.ReadAsStringAsync();
Items_ForList = JsonConvert.DeserializeObject<ObservableCollection<GBSLanguage_ForList>>(ls_Response);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
finally
{
this.IsDataLoaded = true;
NotifyPropertyChanged("GBSLanguages_ForList");
}
}
/// <summary>
/// Notifies subscribers that a property has changed.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I have all my ViewModels in a static class so I only ever get one instance of them as follows:
namespace WebAPIHelper
{
public static class ViewModelObjects
{
private static VM_Languages iobj_Languages;
public static VM_Languages Languages
{
get
{
if (iobj_Languages == null)
iobj_Languages = new VM_Languages();
return iobj_Languages;
}
}
}
}
In my on appearing code of my main page I have the following call to retrieve the data from the WebAPI
protected override async void OnAppearing()
{
Device.BeginInvokeOnMainThread(() =>
{
if (!ViewModelObjects.Languages.IsDataLoaded)
ViewModelObjects.Languages.LoadData();
});
//If the DOW and Language data are not loaded yet - wait
while (!ViewModelObjects.Languages.IsDataLoaded)
{
await Task.Delay(1000);
}
}
The problem is when I attempt to use my WebAPICaller class, it appears to crash on the line. I never get a return from it. No exceptions are ever raised and my program never continues.
lobj_HTTPResponse = await lobj_HTTPClient.GetAsync(ps_URI);
If I make what I believe to be the exact same call from my ViewModel, it works. (I have both the call to the WebAPICaller class as well as a direct call to GetAsync in the View Model so you can test it out by commenting and uncommenting.)
Any idea as to what I am doing wrong?
Link to full sample project:
https://1drv.ms/u/s!Ams6cZUzaeQy3M8uGAuaGggMt0Fi-A
So here is what I found. It seems that awaiting the HTTPClient.GetAsync was causing the error. (Pretty sure the thread was blocked.) So to start with I took out the await and added code to delay the task when HTTPClient's return task was not completed.
var lobj_Result = lobj_HTTPClient.GetAsync(ps_URI);
while (!lobj_Result.IsCompleted)
{
Task.Delay(100);
}
Because I no longer await the call in the LoadData method, I was able to remove the async Task declaration and simply make it a method.
public void LoadData()
{
HttpClient lobj_HTTPClient = null;
HttpResponseMessage lobj_HTTPResponse = null;
string ls_Response = "";
try
{
IsDataLoaded = false;
string ls_WorkLanguageURI = ic_LanguagesAPIUrl;
//Get the Days of the Week
lobj_HTTPClient = new HttpClient(new NativeMessageHandler());
lobj_HTTPClient.BaseAddress = new Uri(App.APIPrefix);
lobj_HTTPClient.DefaultRequestHeaders.Accept.Clear();
lobj_HTTPClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//This call will not work
WebAPICaller lobj_APICaller = new WebAPICaller();
ls_Response = lobj_APICaller.CallWebService(ls_WorkLanguageURI).Result;
if (ls_Response.Trim().Length > 0)
{
if (this.Items_ForList != null)
this.Items_ForList.Clear();
Items_ForList = JsonConvert.DeserializeObject<ObservableCollection<GBSLanguage_ForList>>(ls_Response);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
finally
{
this.IsDataLoaded = true;
NotifyPropertyChanged("GBSLanguages_ForList");
}
}
The I could remove the async from my on appearing event and simply call the loaddata event synchronously.
if (!ViewModelObjects.Languages.IsDataLoaded)
ViewModelObjects.Languages.LoadData();
After making all these changes, the desired result was achieved. Everything ran in synchronous manner (I know the call to GetAsync function of the HTTPClient is still async) and did not return until the results were retrieve and loaded into the object.
I hope this helps other people out. :)
There are a couple of things I see going on. First, the commented-out code:
//This call will not work
//WebAPICaller lobj_APICaller = new WebAPICaller();
//ls_Response = lobj_APICaller.CallWebService(ls_WorkLanguageURI).Result;
Is using .Result instead of await. This may be the root of your problem. Using .Result should be avoided in general, just use await. See the answer here Await on a completed task same as task.Result? for more details on why.
The second thing is that you are already on the UI thread when OnAppearing is called, so there is no need to call Device.BeginInvokeOnMainThread. What you're doing in that method currently is equivalent to:
protected override async void OnAppearing()
{
if (!ViewModelObjects.Languages.IsDataLoaded)
await ViewModelObjects.Languages.LoadData();
}
The next question is whether or not it's a great idea to be doing this in OnAppearing(). This can cause your app to seem non-responsive.
The general use of Device.BeginInvokeOnMainThread is for when you don't know if you're currently on the main thread, but these UI event handlers always are called on the main thread by the Xamarin platform.
Based on the source code on github
void IPageController.SendAppearing()
{
if (_hasAppeared)
return;
_hasAppeared = true;
if (IsBusy)
MessagingCenter.Send(this, BusySetSignalName, true);
OnAppearing();
EventHandler handler = Appearing;
if (handler != null)
handler(this, EventArgs.Empty);
var pageContainer = this as IPageContainer<Page>;
((IPageController)pageContainer?.CurrentPage)?.SendAppearing();
}
there is still a way to do this with async/await approach.
You will notice that the OnAppearing method is called just before the event is triggered.
Subscribe to the Appearing event of the page/view
protected override void OnAppearing() {
this.Appearing += Page_Appearing;
}
and create an async method like you did originally but this time have it on an actual even handler
private async void Page_Appearing(object sender, EventArgs e) {
if (!ViewModelObjects.Languages.IsDataLoaded)
await ViewModelObjects.Languages.LoadData();
//unsubscribing from the event
this.Appearing -= Page_Appearing;
}
This way there is no need to busy wait delay the thread while waiting for the task to complete.
Related
The Issue: After using HttpClient correctly (as a static single instance) over a long time, ConcurrentStack+Node instances consistently grow in size causing increased memory usage. Edit: This seems related to the dispatcher called in SetText and AddText.
The Use-Case: I am polling a device on my local network through HTTP transmission. I use System.Threading.Timer to control the frequency of my requests. The callback for the polling timer is async void so that the await operator can be used for continuation control. The default window dispatcher is used to display text results after the HttpRequest is called.
The Code for Recreation: .NET Framework 4.5 ; WPF ; All standard settings with a single TextBlock created in MainWindow with x:Name="Debug"
using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace LeakIsolator
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
static MainWindow()
{
WebClient.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore);
HttpClient.DefaultRequestHeaders.CacheControl = new System.Net.Http.Headers.CacheControlHeaderValue()
{
NoStore = true,
NoCache = true
};
}
public MainWindow()
{
InitializeComponent();
Timer = new Timer(Poll, null, Timeout.Infinite, Timeout.Infinite);
Loaded += MainWindow_Loaded;
}
private static HttpClient HttpClient { get; } = new HttpClient();
private static WebClient WebClient { get; } = new WebClient();
private Timer Timer { get; }
private void SetText(string text)
{
Dispatcher.Invoke(() => Debug.Text = text);
}
private void AddText(string text)
{
Dispatcher.Invoke(() => Debug.Text += text);
}
private async void Poll(object a)
{
try
{
SetText("Getting status...");
SetText(await GetResponseStringHttpClient("http://10.10.10.87/status"));
}
catch (Exception e)
{
SetText($"Exception. {e.Message}");
}
finally
{
Start();
}
}
private async Task<string> GetResponseStringWebClient(string address)
{
return await WebClient.DownloadStringTaskAsync(address);
}
private async Task<string> GetResponseStringHttpClient(string address)
{
using (var response = await HttpClient.GetAsync(address))
{
if (response.IsSuccessStatusCode)
{
AddText(" Success...");
return await response.Content.ReadAsStringAsync();
}
else
{
AddText(" Fail.");
return null;
}
}
}
private void Start()
{
Timer.Change(1000, Timeout.Infinite);
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Start();
}
}
}
Using ANTS memory profiler, and running the application for 37 minutes in the most recent test, 2.618 MB (small for now, but this is a long-running application) inaccessible-to-me bytes are allocated by a PinnableBufferCache, presumably stored in the gen 2 heap.
I am not sure if this is a memory leak or is a part of something I do not understand. Why am I seeing these memory increases and why am I unable to dictate the local caching behavior of HttpClient and WebClient?
I would guess AddText, you could try commenting out the code in that method and see if the memory leak goes away.
How can i monitor windows services using c# and i also have to save those services name, started time and services end time using in a CSV file. If any new services started than it should automatically write services name, started time and services end time using in existing CSV file.
In case someone is looking for a solution to this in 2021, you can do this using a service controller, async task and the WaitForStatus() method:
Update: I realized my initial solution would not work so I rewrote it completely:
CLASS DEFINITION
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.ServiceProcess; // not referenced by default
public class ExtendedServiceController: ServiceController
{
public event EventHandler<ServiceStatusEventArgs> StatusChanged;
private Dictionary<ServiceControllerStatus, Task> _tasks = new Dictionary<ServiceControllerStatus, Task>();
new public ServiceControllerStatus Status
{
get
{
base.Refresh();
return base.Status;
}
}
public ExtendedServiceController(string ServiceName): base(ServiceName)
{
foreach (ServiceControllerStatus status in Enum.GetValues(typeof(ServiceControllerStatus)))
{
_tasks.Add(status, null);
}
StartListening();
}
private void StartListening()
{
foreach (ServiceControllerStatus status in Enum.GetValues(typeof(ServiceControllerStatus)))
{
if (this.Status != status && (_tasks[status] == null || _tasks[status].IsCompleted))
{
_tasks[status] = Task.Run(() =>
{
try
{
base.WaitForStatus(status);
OnStatusChanged(new ServiceStatusEventArgs(status));
StartListening();
}
catch
{
// You can either raise another event here with the exception or ignore it since it most likely means the service was uninstalled/lost communication
}
});
}
}
}
protected virtual void OnStatusChanged(ServiceStatusEventArgs e)
{
EventHandler<ServiceStatusEventArgs> handler = StatusChanged;
handler?.Invoke(this, e);
}
}
public class ServiceStatusEventArgs : EventArgs
{
public ServiceControllerStatus Status { get; private set; }
public ServiceStatusEventArgs(ServiceControllerStatus Status)
{
this.Status = Status;
}
}
USAGE
static void Main(string[] args)
{
ExtendedServiceController xServiceController = new ExtendedServiceController("myService");
xServiceController.StatusChanged += xServiceController_StatusChanged;
Console.Read();
// Added bonus since the class inherits from ServiceController, you can use it to control the service as well.
}
// This event handler will catch service status changes externally as well
private static void xServiceController_StatusChanged(object sender, ServiceStatusEventArgs e)
{
Console.WriteLine("Status Changed: " + e.Status);
}
You can list running services using ServiceController or ManagementObjectSearcher.
Here is a sample using the ManagementObjectSearcher :
using System.Management;
...
StringBuilder sb = new StringBuilder();
string format = "{0},{1},{2},{3},{4}";
// Header line
sb.AppendFormat(format, "DisplayName",
"ServiceName",
"Status",
"ProcessId",
"PathName");
sb.AppendLine();
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("SELECT * FROM Win32_Service");
foreach( ManagementObject result in searcher.Get() )
{
sb.AppendFormat(format, result["DisplayName"],
result["Name"],
result["State"],
result["ProcessId"],
result["PathName"]
);
sb.AppendLine();
}
File.WriteAllText(
#"C:\temp\ManagementObjectSearcher_services.csv",
sb.ToString()
);
For getting start and stop times it looks like you have to query the Windows Event Log.
This blog post show how you can monitor the event log to get notified when a service is stopped or started:
https://dotnetcodr.com/2014/12/02/getting-notified-by-a-windows-service-status-change-in-c-net/
I'm developing a windows phone 8.1 application.
I'm making use of Twilio to send text messages at one go with Azure services.
Thing is that I have to make a post request to a URL. When I use Fiddler to make the request, everything works great! However, I don't know how to reproduce the same using C#. I tried using HTTP Client and all but don't know how to make it work good.
Could anyone kindly help me?
Thanking you in advance.
using SDKTemplate;
using Windows.UI.Xaml.Controls;using System;
using System.Threading;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Navigation;
using Windows.Web.Http;
namespace SDKSample.HttpClientSample
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class Scenario6 : Page, IDisposable
{
// A pointer back to the main page. This is needed if you want to call methods in MainPage such
// as NotifyUser()
MainPage rootPage = MainPage.Current;
private HttpClient httpClient;
private CancellationTokenSource cts;
public Scenario6()
{
this.InitializeComponent();
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached. The Parameter
/// property is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
Helpers.CreateHttpClient(ref httpClient);
cts = new CancellationTokenSource();
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
// If the navigation is external to the app do not clean up.
// This can occur on Phone when suspending the app.
if (e.NavigationMode == NavigationMode.Forward && e.Uri == null)
{
return;
}
Dispose();
}
private async void Start_Click(object sender, RoutedEventArgs e)
{
Uri resourceAddress;
// The value of 'AddressField' is set by the user and is therefore untrusted input. If we can't create a
// valid, absolute URI, we'll notify the user about the incorrect input.
if (!Helpers.TryGetUri(AddressField.Text, out resourceAddress))
{
rootPage.NotifyUser("Invalid URI.", NotifyType.ErrorMessage);
return;
}
Helpers.ScenarioStarted(StartButton, CancelButton, OutputField);
rootPage.NotifyUser("In progress", NotifyType.StatusMessage);
try
{
HttpMultipartFormDataContent form = new HttpMultipartFormDataContent();
form.Add(new HttpStringContent(RequestBodyField.Text), "data");
HttpResponseMessage response = await httpClient.PostAsync(resourceAddress, form).AsTask(cts.Token);
await Helpers.DisplayTextResultAsync(response, OutputField, cts.Token);
rootPage.NotifyUser("Completed", NotifyType.StatusMessage);
}
catch (TaskCanceledException)
{
rootPage.NotifyUser("Request canceled.", NotifyType.ErrorMessage);
}
catch (Exception ex)
{
rootPage.NotifyUser("Error: " + ex.Message, NotifyType.ErrorMessage);
}
finally
{
Helpers.ScenarioCompleted(StartButton, CancelButton);
}
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
cts.Cancel();
cts.Dispose();
// Re-create the CancellationTokenSource.
cts = new CancellationTokenSource();
}
public void Dispose()
{
if (httpClient != null)
{
httpClient.Dispose();
httpClient = null;
}
if (cts != null)
{
cts.Dispose();
cts = null;
}
}
}
}
So okay,
I've tried that the below. It seems to be sending the request but without a body in json. How can I send a request in JSON using the following code?
using (var client = new HttpClient())
{
var values = new Dictionary<string, string>
{
{ "thing1", "hello" },
{ "thing2", "world" }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("http://www.example.com/recepticle.aspx", content);
var responseString = await response.Content.ReadAsStringAsync();
}
Here my new code which is not working (Getting error cannot convert cannot convert from 'windows.web.http.http stringcontent' to 'system.net.http.httpcontent' on line System.Net.Http.HttpResponseMessage response = await client.PostAsync("http://twilionotifs.azure-mobile.net/api/notificationqueue", stringContent);):
private async void Button_Click(object sender, RoutedEventArgs e)
{
HttpStringContent stringContent = new HttpStringContent("{ \"firstName\": \"John\" }",
Windows.Storage.Streams.UnicodeEncoding.Utf8,
"application/json");
System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
System.Net.Http.HttpResponseMessage response = await client.PostAsync("http://twilionotifs.azure-mobile.net/api/notificationqueue", stringContent);
}
}
I'm struggling with trying to find the best way to implement WCF retries. I'm hoping to make the client experience as clean as possible. There are two approaches of which I'm aware (see below). My question is: "Is there a third approach that I'm missing? Maybe one that's the generally accepted way of doing this?"
Approach #1: Create a proxy that implements the service interface. For each call to the proxy, implement retries.
public class Proxy : ISomeWcfServiceInterface
{
public int Foo(int snurl)
{
return MakeWcfCall<int>(() => _channel.Foo(snurl));
}
public string Bar(string snuh)
{
return MakeWcfCall<string>(() => _channel.Bar(snuh));
}
private static T MakeWcfCall<T>(Func<T> func)
{
// Invoke the func and implement retries.
}
}
Approach #2: Change MakeWcfCall() (above) to public, and have the consuming code send the func directly.
What I don't like about approach #1 is having to update the Proxy class every time the interface changes.
What I don't like about approach #2 is the client having to wrap their call in a func.
Am I missing a better way?
EDIT
I've posted an answer here (see below), based on the accepted answer that pointed me in the right direction. I thought I'd share my code, in an answer, to help someone work through what I had to work through. Hope it helps.
I have done this very type of thing and I solved this problem using .net's RealProxy class.
Using RealProxy, you can create an actual proxy at runtime using your provided interface. From the calling code it is as if they are using an IFoo channel, but in fact it is a IFoo to the proxy and then you get a chance to intercept the calls to any method, property, constructors, etc…
Deriving from RealProxy, you can then override the Invoke method to intercept calls to the WCF methods and handle CommunicationException, etc.
Note: This shouldn't be the accepted answer, but I wanted to post the solution in case it helps others. Jim's answer pointed me in this direction.
First, the consuming code, showing how it works:
static void Main(string[] args)
{
var channelFactory = new WcfChannelFactory<IPrestoService>(new NetTcpBinding());
var endpointAddress = ConfigurationManager.AppSettings["endpointAddress"];
// The call to CreateChannel() actually returns a proxy that can intercept calls to the
// service. This is done so that the proxy can retry on communication failures.
IPrestoService prestoService = channelFactory.CreateChannel(new EndpointAddress(endpointAddress));
Console.WriteLine("Enter some information to echo to the Presto service:");
string message = Console.ReadLine();
string returnMessage = prestoService.Echo(message);
Console.WriteLine("Presto responds: {0}", returnMessage);
Console.WriteLine("Press any key to stop the program.");
Console.ReadKey();
}
The WcfChannelFactory:
public class WcfChannelFactory<T> : ChannelFactory<T> where T : class
{
public WcfChannelFactory(Binding binding) : base(binding) {}
public T CreateBaseChannel()
{
return base.CreateChannel(this.Endpoint.Address, null);
}
public override T CreateChannel(EndpointAddress address, Uri via)
{
// This is where the magic happens. We don't really return a channel here;
// we return WcfClientProxy.GetTransparentProxy(). That class will now
// have the chance to intercept calls to the service.
this.Endpoint.Address = address;
var proxy = new WcfClientProxy<T>(this);
return proxy.GetTransparentProxy() as T;
}
}
The WcfClientProxy: (This is where we intercept and retry.)
public class WcfClientProxy<T> : RealProxy where T : class
{
private WcfChannelFactory<T> _channelFactory;
public WcfClientProxy(WcfChannelFactory<T> channelFactory) : base(typeof(T))
{
this._channelFactory = channelFactory;
}
public override IMessage Invoke(IMessage msg)
{
// When a service method gets called, we intercept it here and call it below with methodBase.Invoke().
var methodCall = msg as IMethodCallMessage;
var methodBase = methodCall.MethodBase;
// We can't call CreateChannel() because that creates an instance of this class,
// and we'd end up with a stack overflow. So, call CreateBaseChannel() to get the
// actual service.
T wcfService = this._channelFactory.CreateBaseChannel();
try
{
var result = methodBase.Invoke(wcfService, methodCall.Args);
return new ReturnMessage(
result, // Operation result
null, // Out arguments
0, // Out arguments count
methodCall.LogicalCallContext, // Call context
methodCall); // Original message
}
catch (FaultException)
{
// Need to specifically catch and rethrow FaultExceptions to bypass the CommunicationException catch.
// This is needed to distinguish between Faults and underlying communication exceptions.
throw;
}
catch (CommunicationException ex)
{
// Handle CommunicationException and implement retries here.
throw new NotImplementedException();
}
}
}
Sequence diagram of a call being intercepted by the proxy:
You can implement generic proxy for example using Castle. There is a good article here http://www.planetgeek.ch/2010/10/13/dynamic-proxy-for-wcf-with-castle-dynamicproxy/. This approach will give user object which implements interface and you will have one class responsible for comunication
Do not mess with generated code because, as you mentioned, it will be generated again so any customization will be overridden.
I see two ways:
Write/generate a partial class for each proxy that contains the retry variation. This is messy because you will still have to adjust it when the proxy changes
Make a custom version of svcutil that allows you to generate a proxy that derives from a different base class and put the retry code in that base class. This is quite some work but it can be done and solves the issue in a robust way.
go through approach 1, then wrape all context event (open, opened, faulted, ...) into event to be exposed by your class proxy, once the communication is faulted then re-create the proxy or call some recursive method inside proxy class. i can share with you some wok i have just tested.
public class DuplexCallBackNotificationIntegrationExtension : IExtension, INotificationPusherCallback {
#region - Field(s) -
private static Timer _Timer = null;
private static readonly object m_SyncRoot = new Object();
private static readonly Guid CMESchedulerApplicationID = Guid.NewGuid();
private static CancellationTokenSource cTokenSource = new CancellationTokenSource();
private static CancellationToken cToken = cTokenSource.Token;
#endregion
#region - Event(s) -
/// <summary>
/// Event fired during Duplex callback.
/// </summary>
public static event EventHandler<CallBackEventArgs> CallBackEvent;
public static event EventHandler<System.EventArgs> InstanceContextOpeningEvent;
public static event EventHandler<System.EventArgs> InstanceContextOpenedEvent;
public static event EventHandler<System.EventArgs> InstanceContextClosingEvent;
public static event EventHandler<System.EventArgs> InstanceContextClosedEvent;
public static event EventHandler<System.EventArgs> InstanceContextFaultedEvent;
#endregion
#region - Property(ies) -
/// <summary>
/// Interface extension designation.
/// </summary>
public string Name {
get {
return "Duplex Call Back Notification Integration Extension.";
}
}
/// <summary>
/// GUI Interface extension.
/// </summary>
public IUIExtension UIExtension {
get {
return null;
}
}
#endregion
#region - Constructor(s) / Finalizer(s) -
/// <summary>
/// Initializes a new instance of the DuplexCallBackNotificationIntegrationExtension class.
/// </summary>
public DuplexCallBackNotificationIntegrationExtension() {
CallDuplexNotificationPusher();
}
#endregion
#region - Delegate Invoker(s) -
void ICommunicationObject_Opening(object sender, System.EventArgs e) {
DefaultLogger.DUPLEXLogger.Info("context_Opening");
this.OnInstanceContextOpening(e);
}
void ICommunicationObject_Opened(object sender, System.EventArgs e) {
DefaultLogger.DUPLEXLogger.Debug("context_Opened");
this.OnInstanceContextOpened(e);
}
void ICommunicationObject_Closing(object sender, System.EventArgs e) {
DefaultLogger.DUPLEXLogger.Debug("context_Closing");
this.OnInstanceContextClosing(e);
}
void ICommunicationObject_Closed(object sender, System.EventArgs e) {
DefaultLogger.DUPLEXLogger.Debug("context_Closed");
this.OnInstanceContextClosed(e);
}
void ICommunicationObject_Faulted(object sender, System.EventArgs e) {
DefaultLogger.DUPLEXLogger.Error("context_Faulted");
this.OnInstanceContextFaulted(e);
if (_Timer != null) {
_Timer.Dispose();
}
IChannel channel = sender as IChannel;
if (channel != null) {
channel.Abort();
channel.Close();
}
DoWorkRoutine(cToken);
}
protected virtual void OnCallBackEvent(Notification objNotification) {
if (CallBackEvent != null) {
CallBackEvent(this, new CallBackEventArgs(objNotification));
}
}
protected virtual void OnInstanceContextOpening(System.EventArgs e) {
if (InstanceContextOpeningEvent != null) {
InstanceContextOpeningEvent(this, e);
}
}
protected virtual void OnInstanceContextOpened(System.EventArgs e) {
if (InstanceContextOpenedEvent != null) {
InstanceContextOpenedEvent(this, e);
}
}
protected virtual void OnInstanceContextClosing(System.EventArgs e) {
if (InstanceContextClosingEvent != null) {
InstanceContextClosingEvent(this, e);
}
}
protected virtual void OnInstanceContextClosed(System.EventArgs e) {
if (InstanceContextClosedEvent != null) {
InstanceContextClosedEvent(this, e);
}
}
protected virtual void OnInstanceContextFaulted(System.EventArgs e) {
if (InstanceContextFaultedEvent != null) {
InstanceContextFaultedEvent(this, e);
}
}
#endregion
#region - IDisposable Member(s) -
#endregion
#region - Private Method(s) -
/// <summary>
///
/// </summary>
void CallDuplexNotificationPusher() {
var routine = Task.Factory.StartNew(() => DoWorkRoutine(cToken), cToken);
cToken.Register(() => cancelNotification());
}
/// <summary>
///
/// </summary>
/// <param name="ct"></param>
void DoWorkRoutine(CancellationToken ct) {
lock (m_SyncRoot) {
var context = new InstanceContext(this);
var proxy = new NotificationPusherClient(context);
ICommunicationObject communicationObject = proxy as ICommunicationObject;
communicationObject.Opening += new System.EventHandler(ICommunicationObject_Opening);
communicationObject.Opened += new System.EventHandler(ICommunicationObject_Opened);
communicationObject.Faulted += new System.EventHandler(ICommunicationObject_Faulted);
communicationObject.Closed += new System.EventHandler(ICommunicationObject_Closed);
communicationObject.Closing += new System.EventHandler(ICommunicationObject_Closing);
try {
proxy.Subscribe(CMESchedulerApplicationID.ToString());
}
catch (Exception ex) {
Logger.HELogger.DefaultLogger.DUPLEXLogger.Error(ex);
switch (communicationObject.State) {
case CommunicationState.Faulted:
proxy.Close();
break;
default:
break;
}
cTokenSource.Cancel();
cTokenSource.Dispose();
cTokenSource = new CancellationTokenSource();
cToken = cTokenSource.Token;
CallDuplexNotificationPusher();
}
bool KeepAliveCallBackEnabled = Properties.Settings.Default.KeepAliveCallBackEnabled;
if (KeepAliveCallBackEnabled) {
_Timer = new Timer(new TimerCallback(delegate(object item) {
DefaultLogger.DUPLEXLogger.Debug(string.Format("._._._._._. New Iteration {0: yyyy MM dd hh mm ss ffff} ._._._._._.", DateTime.Now.ToUniversalTime().ToString()));
DBNotificationPusherService.Acknowledgment reply = DBNotificationPusherService.Acknowledgment.NAK;
try {
reply = proxy.KeepAlive();
}
catch (Exception ex) {
DefaultLogger.DUPLEXLogger.Error(ex);
switch (communicationObject.State) {
case CommunicationState.Faulted:
case CommunicationState.Closed:
proxy.Abort();
ICommunicationObject_Faulted(null, null);
break;
default:
break;
}
}
DefaultLogger.DUPLEXLogger.Debug(string.Format("Acknowledgment = {0}.", reply.ToString()));
_Timer.Change(Properties.Settings.Default.KeepAliveCallBackTimerInterval, Timeout.Infinite);
}), null, Properties.Settings.Default.KeepAliveCallBackTimerInterval, Timeout.Infinite);
}
}
}
/// <summary>
///
/// </summary>
void cancelNotification() {
DefaultLogger.DUPLEXLogger.Warn("Cancellation request made!!");
}
#endregion
#region - Public Method(s) -
/// <summary>
/// Fire OnCallBackEvent event and fill automatic-recording collection with newest
/// </summary>
/// <param name="action"></param>
public void SendNotification(Notification objNotification) {
// Fire event callback.
OnCallBackEvent(objNotification);
}
#endregion
#region - Callback(s) -
private void OnAsyncExecutionComplete(IAsyncResult result) {
}
#endregion
}
Just wrap all service calls in a function, taking a delegate that would execute the passed delegate the amount of time necessary
internal R ExecuteServiceMethod<I, R>(Func<I, R> serviceCall, string userName, string password) {
//Note all clients have the name Manager, but this isn't a problem as they get resolved
//by type
ChannelFactory<I> factory = new ChannelFactory<I>("Manager");
factory.Credentials.UserName.UserName = userName;
factory.Credentials.UserName.Password = password;
I manager = factory.CreateChannel();
//Wrap below in a retry loop
return serviceCall.Invoke(manager);
}
So im trying to make a data-grid that displays some information about local window services, mine in particular, I would like to have the display name and status of the service, and then have a button to click to start or stop. I can link the button method up fine, but the service status does not change, any suggestions an how to make this property observable to the datagrid, and also possible change the button on the fly from start to stop based on the status, secondly I would like to make the stop command be be a button command if possibe.
Any suggestions?
You need to wrap your the service into your own class that implements INotifyPropertyChanged. As you start/stop the service, raise property change event on that instance.
This is what I ended up implamenting. It works pretty well for the most part, however I up for any code suggestions anyone might.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.ServiceProcess;
namespace v7quickbar
{
class NotifiableServiceController : INotifyPropertyChanged
{
private ServiceController m_oServiceController = null;
private System.Timers.Timer m_oServiceCheckTimer = new System.Timers.Timer();
public ServiceControllerStatus Status { get { return this.m_oServiceController.Status; } }
public string DisplayName { get { return this.m_oServiceController.DisplayName; } }
public string ServiceName { get { return this.m_oServiceController.ServiceName; } }
public bool CanStop { get { return this.m_oServiceController.CanStop; } }
public NotifiableServiceController(ServiceController oService)
{
CreateObject(oService, TimeSpan.FromSeconds(.5));
}
public NotifiableServiceController(ServiceController oService, TimeSpan oInterval)
{
CreateObject(oService, oInterval);
}
private void CreateObject(ServiceController oService, TimeSpan oInterval)
{
m_oServiceController = oService;
m_oServiceCheckTimer.Interval = oInterval.TotalMilliseconds;
m_oServiceCheckTimer.Elapsed += new System.Timers.ElapsedEventHandler(m_oServiceCheckTimer_Elapsed);
m_oServiceCheckTimer.Start();
}
public void Start()
{
try
{
this.m_oServiceController.Start();
this.m_oServiceController.WaitForStatus(ServiceControllerStatus.Running);
}
catch (Exception)
{
}
}
public void Stop()
{
try
{
this.m_oServiceController.Stop();
this.m_oServiceController.WaitForStatus(ServiceControllerStatus.Stopped);
}
catch (Exception)
{
}
}
public void Restart()
{
try
{
if (m_oServiceController.CanStop && (m_oServiceController.Status == ServiceControllerStatus.Running || m_oServiceController.Status == ServiceControllerStatus.Paused))
{
this.Stop();
this.m_oServiceController.WaitForStatus(ServiceControllerStatus.Stopped);
}
if (m_oServiceController.Status == ServiceControllerStatus.Stopped)
{
this.Start();
this.m_oServiceController.WaitForStatus(ServiceControllerStatus.Running);
}
}
catch (Exception)
{
}
}
void m_oServiceCheckTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
ServiceControllerStatus oCurrentStatus = m_oServiceController.Status;
m_oServiceController.Refresh();
if (oCurrentStatus != m_oServiceController.Status)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Status"));
}
}
public static IEnumerable<NotifiableServiceController> GetServices()
{
List<NotifiableServiceController> oaServices = new List<NotifiableServiceController>();
foreach (ServiceController sc in ServiceController.GetServices())
{
oaServices.Add(new NotifiableServiceController(sc));
}
return oaServices;
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
Sadly because ServiceController.GetServices() call would always return an array, we have to have DispatcherTimer and in its tick, make call to ServiceController.GetServices() and raise notify property changed for that property that holds the array of services.
Making it observable for the sake of observability isnt practical right? We wont gain any advantage out of it anyways.