Last time I wrote about Health Monitoring was during 2008 and since then I ‘m using, on a daily basis, a complex Health Monitoring EventLog provider to aggregate WebEvents from specific ASP.NET web applications.
Last year I found ELMAH (Error Logging Module and Handler) project and became a fan of the concept:
“[…] facility that is completely pluggable. It can be dynamically added to a running ASP.NET web application, or even all ASP.NET web applications on a machine, without any need for re-compilation or re-deployment.”
It didn’t take much time until I start thinking about releasing a version of my EventLog WebEvents aggregator.
I’m now happy to announce that I finally made this project available at Codeplex and name it: ELWEA - EventLog WebEvent Aggregator for ASP.NET.
While different in nature this project main goal is pretty similar to the ELMAH project: keep an ASP.NET web application error tracking isolated and easily accessible.
What makes this project different from many other like ELMAH is the fact that it's not focused on handling and persisting error info. This project main goal is to aggregate all EventLog data belong to a specific web application under a specific EventLog source name.
| Before | After |
 |  |
I’m sure that web developer and web site administrators will love it but I think that also IT pros that manage shared hosting environments will use it.
Visit here for a more complete description and here for the project documentation.
To make your live even simpler I’m planning to make a NuGet package available for ELWEA. Keep an eye on this.
Enjoy it!
From time to time I end-up finding really old hidden gems and a few days ago I found another one.
IList System.Collections.ArrayList.ReadOnly(IList list)
This amazing method is available since the beginning (.NET 1.0).
I always complain about the small support for ReadOnly lists and collections and I have no clue why I miss this one.
For those of you that have to maintain and extend legacy applications prior to ASP.NET 2.0 SP2 this could be a very useful finding.
Hi all,
At work we have about 5000 PC's that are mainly our commercial network. All those PC use intranet services and web applications.
Until a few months ago IE6 was our corporate browser (yep) but we ended moving to IE8.
A few days ago someone came to me saying that their application appears to have some problem because every time they click some HTML buttons the server gets two hits.
Also they are able to say that this didn't happen when they used the IE6.
In the faulty application the buttons that trigger two posts were System.Web.UI.HtmlControls.HtmlButton controls with Click event handlers registered.
After some research I found that the default type attribute value for HTML BUTTON element has changed in IE8 and later (here).
- In IE7 and earlier the default value is 'button'
- In IE8 and later the default value is 'submit'
This changed in browser default behavior was not reflected in the Server-Control that renders it: it doesn't set the type attribute value to button when a postback is required.
Without such change, every time we attached a Click event handler to a HtmlButton we endup with two posts: one triggered by the _doPostback function and another from the form itself (caused by the default type='submit' value.).
I easily bypass this problem by setting the type attribute value to 'button' in my base control.
Even so, I think that this change should be done by the framework itself when a postback is required in a HtmlButton.
After some 'reflection' I propose changing the System.Web.UI.Util.WriteOnClickAttribute method to do the job.
Such change would ensure the some behavior independent of the browser version.
If you have another workaround or you think I'm completely wrong please let me know.
Also if you think this is a real problem that should be solved then visit this Connect entry I create.
Microsoft has published a Security Advisory (2416728) about a security vulnerability in ASP.NET on Saturday, September 18th. This vulnerability exists in all versions of ASP.NET and was publically disclosed late Friday at a security conference.
Scott Guthrie has provided information on workarounds (please see Important: ASP.NET Security Vulnerability and ASP.NET Security Vulnerability) to prevent attackers from using this vulnerability against their ASP.NET applications.
To help with Microsoft’s response to the new padding oracle vulnerability, a new forum was also set up: Security Vulnerability.
Microsoft has now announced the release of an out-of-band security update to address the ASP.NET Security Vulnerability.
Applying the update addresses the ASP.NET Security Vulnerability, and once the update is applied to your system the workarounds Scott has previously blogged about will no longer be required. But, until the update has been installed, those workarounds must be used.
You can learn more about this security update release from this reading the Microsoft Security Response Center Blog Post as well as the official Advance Notification Bulletin.
Important Links:
Microsoft is now hosting jQuery UI on the Microsoft Ajax CDN (uncompressed and compressed versions) as well as all the pre-built themes.
Remember that CDN using is free and no registration is required.
For more information check out Stephen Walther’s announcement from Microsoft.
It’s now official and public. I was reward with the MVP award in ASP/ASP.NET.
This award is a huge responsibility but is also a sign that shows the right way.
During last year I learned many things and also made some mistakes.
What can say about next year is that my guidelines won’t change: learn, help and share.
Thanks everyone.
The ControlAdapter is available since .NET framework version 2.0 and his main goal is to adapt and customize a control render in order to achieve a specific behavior or layout. This customization is done without changing the base control.
A ControlAdapter is commonly used to custom render for specific platforms like Mobile.
In this particular case the ControlAdapter was used to add a specific behavior to a Control. In this post I will use one adapter to add a Captcha to all WeblogPostCommentForm controls within pontonetpt.com CommunityServer instance.
The Challenge
The ControlAdapter complexity is usually associated with the complexity/structure of is base control. This case is precisely one of those since base control dynamically load his content (controls) thru several ITemplate.
Those of you who already played with ITemplate knows that while it is an excellent option for control composition it also brings to the table a big issue:
“Controls defined within a template are not available for manipulation until they are instantiated inside another control.”
While analyzing the WeblogPostCommentForm control I found that he uses the ITemplate technique to compose it’s layout and unfortunately I also found that the template content vary from theme to theme. This could have been a problem but luckily WeblogPostCommentForm control template content always contains a submit button with a well known ID (at least I can assume that there are a well known set of IDs).
Using this submit button as anchor it’s possible to add the Captcha controls in the correct place.
Another important finding was that WeblogPostCommentForm control inherits from the WrappedFormBase control which is the base control for all CommunityServer input forms.
Knowing this inheritance link the main goal has changed to became the creation of a base ControlAdapter that could be extended and customized to allow adding Captcha to:
- post comments form
- contact form
- user creation form.
And, with this mind set, I decided to used the following ControlAdapter base class signature :
public abstract class WrappedFormBaseCaptchaAdapter<T> : ControlAdapter where T : WrappedFormBase
{
}Great, but there are still many to do …
Captcha
The Captcha will be assembled with:
- A dynamically generated image with a set of random numbers
- A TextBox control where the image number will be inserted
- A Validator control to validate whether TextBox numbers match the image numbers
This is a common Captcha implementation, is not rocket science and don’t bring any additional problem. The main problem, as told before, is to find the correct anchor control to ensure a correct Captcha control injection.
The anchor control can vary by:
Implementation
To support this dynamic scenario I choose to use the following implementation:
private List<string> _validAnchorIds = null;
protected virtual List<string> ValidAnchorIds
{
get
{
if (this._validAnchorIds == null)
{
this._validAnchorIds = new List<string>();
this._validAnchorIds.Add("btnSubmit");
}
return this._validAnchorIds;
}
}
private Control GetAnchorControl(T wrapper)
{
if (this.ValidAnchorIds == null || this.ValidAnchorIds.Count == 0)
{
throw new ArgumentException("Cannot be null or empty", "validAnchorNames");
}
var q = from anchorId in this.ValidAnchorIds
let anchorControl = CSControlUtility.Instance().FindControl(wrapper, anchorId)
where anchorControl != null
select anchorControl;
return q.FirstOrDefault();
}
I can now, using the ValidAnchorIds property, configure a set of valid anchor control Ids.
The GetAnchorControl method searches for a valid anchor control within the set of valid control Ids. Here, some of you may question why to use a LINQ To Objects expression, but the important here is to notice the usage of CSControlUtility.Instance().FindControl CommunityServer method. I want to build on top of CommunityServer not to reinvent the wheel.
Assuming that an anchor control was found, it’s now possible to inject the Captcha at the correct place. This not something new, we do this all the time when creating server controls or adding dynamic controls:
protected sealed override void CreateChildControls()
{
base.CreateChildControls();
if (this.IsCaptchaRequired)
{
T wrapper = base.Control as T;
if (wrapper != null)
{
Control anchorControl = GetAnchorControl(wrapper);
if (anchorControl != null)
{
Panel phCaptcha = new Panel {CssClass = "CommonFormField", ID = "Captcha"};
int index = anchorControl.Parent.Controls.IndexOf(anchorControl);
anchorControl.Parent.Controls.AddAt(index, phCaptcha);
CaptchaConfiguration.DefaultProvider.AddCaptchaControls(
phCaptcha,
GetValidationGroup(wrapper, anchorControl));
}
}
}
}
Here you can see a new entity in action: a provider. This is a CaptchaProvider class instance and is only goal is to create the Captcha itself and do everything else is needed to ensure is correct operation.
public abstract class CaptchaProvider : ProviderBase
{
public abstract void AddCaptchaControls(Panel captchaPanel, string validationGroup);
}
You can create your own specific CaptchaProvider class to use different Captcha strategies including the use of existing Captcha services like ReCaptcha.
Once the generic ControlAdapter was created became extremely easy to created a specific one. Here is the specific ControlAdapter for the WeblogPostCommentForm control:
public class WeblogPostCommentFormCaptchaAdapter : WrappedFormBaseCaptchaAdapter<WrappedFormBase>
{
#region Overriden Methods
protected override List<string> ValidAnchorIds
{
get
{
List<string> validAnchorNames = base.ValidAnchorIds;
validAnchorNames.Add("CommentSubmit");
return validAnchorNames;
}
}
protected override string DefaultValidationGroup
{
get { return "CreateCommentForm"; }
}
#endregion Overriden Methods
}
Configuration
This is the magic step.
Without changing the original pages and keeping the application original assemblies untouched we are going to add a new behavior to the CommunityServer application.
To glue everything together you must follow this steps:
- Add the following configuration to default.browser file:
<?xml version='1.0' encoding='utf-8'?>
<browsers>
<browser refID="Default">
<controlAdapters>
<!-- Adapter for the WeblogPostCommentForm control in order to add the Captcha and prevent SPAM comments -->
<adapter controlType="CommunityServer.Blogs.Controls.WeblogPostCommentForm" adapterType="NunoGomes.CommunityServer.Components.WeblogPostCommentFormCaptchaAdapter, NunoGomes.CommunityServer" />
</controlAdapters>
</browser>
</browsers>
- Add the following configuration to web.config file:
<configuration>
<configSections>
<!-- New section for Captcha providers configuration -->
<section name="communityServer.Captcha" type="NunoGomes.CommunityServer.Captcha.Configuration.CaptchaSection" />
</configSections>
<!-- Configuring a simple Captcha provider -->
<communityServer.Captcha defaultProvider="simpleCaptcha">
<providers>
<add name="simpleCaptcha" type="NunoGomes.CommunityServer.Captcha.Providers.SimpleCaptchaProvider, NunoGomes.CommunityServer"
imageUrl="~/captcha.ashx"
enabled="true"
passPhrase="_YourPassPhrase_"
saltValue="_YourSaltValue_"
hashAlgorithm="SHA1"
passwordIterations="3"
keySize="256"
initVector="_YourInitVectorWithExactly_16_Bytes_"
/>
</providers>
</communityServer.Captcha> <system.web>
<httpHandlers>
<!-- The Captcha Image handler used by the simple Captcha provider -->
<add verb="GET" path="captcha.ashx" type="NunoGomes.CommunityServer.Captcha.Providers.SimpleCaptchaProviderImageHandler, NunoGomes.CommunityServer" />
</httpHandlers>
</system.web>
<system.webServer>
<handlers accessPolicy="Read, Write, Script, Execute">
<!-- The Captcha Image handler used by the simple Captcha provider -->
<add verb="GET" name="captcha" path="captcha.ashx" type="NunoGomes.CommunityServer.Captcha.Providers.SimpleCaptchaProviderImageHandler, NunoGomes.CommunityServer" />
</handlers>
</system.webServer>
</configuration>
Conclusion
Building a ControlAdapter can be complex but the reward is his ability to allows us, thru configuration changes, to modify an application render and/or behavior.
You can see this ControlAdapter in action here and here (anonymous required).
A complete solution is available in “CommunityServer Extensions” Codeplex project.
The long waiting, for RedGate to release the new official .NET Reflector version, is finally ended.
I must say that as far as I know (and I was an early adopter and usability tester), RedGate took special care with this new version, specially the new Reflector Pro version.
This new product (€145) integrates the technology of .NET Reflector into Visual Studio to let you debug third-party code even if you don't have the source.
You can say that this can also be done without this product, and it’s absolutely true, but the simplicity how the integration with Visual Studio (2005, 2008 or 2010) is done worth it .
I use this tool since the beta versions and I’m addicted ;) … try it for yourself.
Let me start by saying that Microsoft don't consider this issue as a problem, as you can see here this is a “by design” behavior.
The problem is well described in the referred Connect feedback and it contains a workaround.
Although simple, the workaround requires you to always register the GridView Sorting event and make the tweak according to the current GridView settings. Well, if are like me you will forget to do it half the times needed.
So, I made a not so simple workaround that will take care of the issue for me.
I override the OnSorting method from GridView so I can handle the GridViewEventArgs instance and override its SortDirection value.
To turn this into a general solution I partially reproduce the ParseSortString method from DataTable to find out if the current SortExpression contains either the ASC or DESC keywords.
Here is the code:
public class GridView : global::System.Web.UI.WebControls.GridView
{
protected override void OnSorting(GridViewSortEventArgs e)
{
if (!string.IsNullOrEmpty(this.SortExpression))
{
if (this.SortExpression.Equals(this.SortExpression))
{
bool isMultipleSortExpression;
SortDirection? sortDirection = GetSortDirection(this.SortExpression, out isMultipleSortExpression);
if (sortDirection.HasValue)
{
// To undo bug in GridView.HandleSort(string sortExpression) and then in GridView.CreateDataSourceSelectArguments()
e.SortDirection = SortDirection.Ascending;
}
}
}
base.OnSorting(e);
}
private SortDirection? GetSortDirection(string sortExpression, out bool isMultipleSortExpression)
{
SortDirection? sortDirection = null;
isMultipleSortExpression = false;
string[] strArray = sortExpression.Split(new char[] { ',' });
for (int i = 0; i < strArray.Length; i++)
{
string strA = strArray[i].Trim();
int length = strA.Length;
if ((length >= 5) && (string.Compare(strA, length - 4, " ASC", 0, 4, StringComparison.OrdinalIgnoreCase) == 0))
{
sortDirection = SortDirection.Ascending;
}
else if ((length >= 6) && (string.Compare(strA, length - 5, " DESC", 0, 5, StringComparison.OrdinalIgnoreCase) == 0))
{
sortDirection = SortDirection.Descending;
}
if (!sortDirection.HasValue)
{
break;
}
}
if (sortDirection.HasValue)
{
if (strArray.Length > 1)
{
isMultipleSortExpression = true;
}
}
return sortDirection;
}
}
Enjoy it.
Next October, 2 ReMIX 09 will be in Portugal.
I’ll be there.
If you are nearby, take the chance and came visit us.
Hope to see you there.
More Posts
Next page »