Technorati-Tags:
telerik,
silverlight
In the first part of this series I describe the approach of this application.
The second part was about a class that reads server performance values.
In this part I’ll show how I expose these performance values.
Since I know that (later) I will use the values with a Silverlight Client I implemented a “Silverlight enabled WCF Service”.
[ServiceContract(Namespace = "http://test.SLServer.Perf")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class PerfSvc {
[OperationContract]
public string GetPerfString() {
return (GetCachedPerfInfo().ToString());
}
[OperationContract]
public PerfInfos GetPerfInfo() {
return(GetCachedPerfInfo());
}
...
There are two methods – one returning a string (summary of the values) and an other one returning the object itself.
In reality it does not return the original object – it serializes it for the client.
The ToString looks like this:
// "nice looking" display
public override string ToString() {
if (ErrMess != "OK") {
return (ErrMess);
}
return (string.Format("CPU: {0:0.00}% RAM - Total: {1}, Free: {2}, Used: {3} / {4:0.00}%",
CPUPercent, RamTotal, RamFree, RamUsed, RamUsedPercent));
}
Both mehods in the webservice use a “cached version” of a PerfInfos object. This has two reasons
- It is always good to cache if possible
- PerformanceCounters (as written before) are not so well performing
- For good calculated values the PerformanceCounter should live a while
For point 1.) caching is perfect since we have an object which keeps the same data for a longer time (a second can be really long in a web server environment – although I guess there won’t be 10k Hits on the servers status
).
private static PerfInfos GetCachedPerfInfo() {
Cache cH = HttpRuntime.Cache;
if (cH == null) {
return (new PerfInfos() { ErrMess = "No Cache" });
}
PerfInfos pI = cH["PerfInf"] as PerfInfos;
if (pI == null) {
pI = new PerfInfos(true, true); //be sure to creat "cache stable instance"
cH.Insert("PerfInf", pI, null, DateTime.Now.AddMinutes(20), Cache.NoSlidingExpiration,
CacheItemPriority.Default, RemovedCallback);
}
pI.LockedReadCurValues();
return pI;
}
public static void RemovedCallback(String strKey, Object oValue, CacheItemRemovedReason rEason) {
PerfInfos pI = oValue as PerfInfos;
if (pI != null) {
pI.CleanUp(); //calls dispose for a "cache stable object"
}
}
The first thing I do is I try to get the cache.
Notice I do not use HttpContext.Current.Cache!!
This has two reasons – I don’t have a always Session (RequiremntsMode is set to Allowed not to required!!) and the second point is that accessing the cache via the context takes a bit longer than the direct access (there is a bunch of code to ensure that the object is there an can be retrieved).
If getting the Cache (why ever) fails I create a PerfInfos instance (which does not generate PerformanceCounters) set ErrMess and return it. I don’t think that I can fail with the Cache – but it seems to be a good way showing how the use of an “ErrMess only PerfInfos” works.
After this I try to get the current object out of the cache. If it is not there I create one.
This time I use this special constructor telling “ImStoredInCache” – which prevents the object from an unwanted Dispose.
Unwanted Dispose?
Yes – WCF calls Dispose for objects used in a service method (under some circumstances).
I never stumbled in this situation – till now – and am still investigating on how to handle this without workarounds.
One thing is clear – if you write a service like this – objects which implement IDisposable will be Disposed by WCF.
Nothing bad (in most cases good – you have no need to take care about such objects) – BUT you must take care about it is seldom situations like this where the object has to survive.
A last note about cache duration and the RemovedCallback. 20 minutes seem to be a good time for me.
I assume that the typical use of this service is a page where a web manager check’s what’s going on. He will watch it for some time – and I guess after about 15 Minutes his job will be done (or he gets bored) and the object is no longer needed.
It would be also an idea to add sliding expiration here. But this does not fit my thoughts - “bored after 15 Minutes” 
The RemoveCallback does what a disposable class expects – it calls dispose. Due to the fact that I “sealed” dispose I wrote an other member which removes the flag and than calls dispose.
//cleanup for no longer cached item
public void CleanUp() {
m_bIamInCache = false;
Dispose();
}
This finishes the section about the web service. The next step will be the consumer – the Silverlight part.