Friday, July 31, 2009

.NET Reflection - Generic Method Type Inference of a Boxed Parameter

Before I begin- this is a fairly lengthy post and if you'd rather not read the whole thing and would just like to know how to pass a boxed object to a generic method and have the run-time properly infer the base type of that object for the method, jump to the bottom.

Today I was working on an ASP.NET MVC project I've been putting together for the past couple weeks and I realized that I have been duplicating a small piece of code over and over out of a falsely perceived necessity. After considering my options and coming up with a simple solution, I began to put it into place- only to quickly discover that there was no way to do what I wanted without some clever Reflection.

My project is set up such that all of my View pages inherit from a class I defined, MasterViewModel, which holds a few top level variables my Master Page requires for rendering. While all of my Views do inherit from MasterViewModel, the majority of them take a derived class, ContentViewPage<T>, which contains a simple generic property housing the Data (simple or complex) for that View. With this design, when I construct my View I always have the Master Page Model items included in the Controller result without having to explicitly add them in my Controller method. This is all well and good, but as you may imagine it became rather annoying to have to wrap the Model data I gave the View in a ContentViewModel<T> object at the end of every Controller call.

public ViewResult SayHello() {
 return View(new ContentViewModel<string>("Hello"));
}

So after a little digging around on Stack Overflow I decided to create myself a couple of helpers to assist in the creation of my ContentViewModel<T> object.

public static class ContentViewModel
{
    public static ContentViewModel<T> Create<T>(T data)
    {
        return new ContentViewModel<T>(data);
    }

    public static readonly MasterViewModel Empty = new MasterViewModel();
}

With this in hand I now need only write ContentViewModel.Create("Hello") to produce the same object I defined above. This seemed like a pretty decent solution at first, but I still wasn't particularly satisfied. Then it occurred to me that if I overrode the View() method of the Controller class and wrapped my objects in a ContentViewModel<T> before calling base.View() I wouldn't ever need to type ContentView.Create() again.

protected override ViewResult View(IView view, object model)
{
    if (model == null)
        return base.View(view, ContentViewModel.Empty);
    if (model is MasterViewModel)
        return base.View(view, model);
    else
        return base.View(view, ContentViewModel.Create(model));
}

protected override ViewResult View(string viewName, string masterName, object model)
{
    if (model == null)
        return base.View(viewName, masterName, ContentViewModel.Empty);
    else if (model is MasterViewModel)
        return base.View(viewName, masterName, model);
    else
        return base.View(viewName, masterName, ContentViewModel.Create(model));
}

Simple and effective, I thought.

Server Error in '/' Application.

The model item passed into the dictionary is of type 'MvcScratch.Controllers.HomeController+ContentViewModel`1[System.Object]' but this dictionary requires a model item of type 'MvcScratch.Controllers.HomeController+ContentViewModel`1[System.String]'.

Or not. This was all working swell before... what went wrong? Well, the View() method's model property is a non-generic Object, that's what went wrong. Usually that wouldn't matter because our View knows what type to cast the Model to based on the Inherits="" attribute of <%@ Page %> and therefore although we downcast our Model for the call to View() in the Controller, the View knows what to expect and upcasts it for us. Trouble is, ContentViewModel.Create() doesn't know that at run time and it only has the type of the parameter to go off of, which, no thanks to the View() method, is an object. Keep in mind, C# still knows the actual type of the object, but at run time ContentViewModel.Create() is unable to infer that until it is within the scope of the method- which is after T has been given a Type. Thus, instead of a ContentViewModel<string> being returned, we get a ContentViewModel<object>. To confirm this:

static void Main(string[] args)
{
    object str = "Hello World";
    Foo(str);
}

static void Foo<T>(T param)
{
    Console.WriteLine("typeof(T): {0}", typeof(T));
    Console.WriteLine("param.GetType(): {0}", param.GetType());
}
Which outputs:
typeof(T):              System.Object
   param.GetType():        System.String

Clearly,we can see that doesn't work quite how we'd like it to, but fear not, we're not out of luck just yet! Using a little bit of reflection we can fix this problem. The solution? Rather than calling ContentViewModel.Create() directly, and allowing the run-time to infer the the generic type T, we're going to invoke it through the class's Type definition and explictly define T's type.

typeof(ContentViewModel).GetMethod("Create").MakeGenericMethod(new Type[] { data.GetType() }).Invoke(null, new object[] { data });

BAM! If you examine this code for a moment or two you should be able to get the general idea. Using Reflection we're grabbing the Type of our method's container and then returning a MethodInfo for that method using its string representation. With that MethodInfo we are calling MethodInfo.MakeGenericMethod() which takes a array of parameter Types (in the order they're defined in the method signature) that will strongly type the method's generic parameters. So instead of T mapping to an 'object' it maps to whatever 'data.GetType()' returns- the actual parameter type. Finally, we invoke the method with a call to MethodInfo.Invoke() which receives an instance of the given Type (or null for a static method) and an Object array containing each parameter in order (or null for a parameterless method). Now if we replace the View() code from above with this call instead we can rest easy knowing that ContentViewModel<T>'s generic type is correctly instantiated.

It is worth noting that of course there is some extra overhead being created here because of our calls into System.Reflection and for that reason I am not entirely sure if this working solution is ideal- it's at least a little iffy and I'd be curious to know what the server performance looks like in a high-traffic environment where this reflection call is being run on every Controller request that doesn't explictly wrap its View() model parameter in a ContentViewModel<T> first, but maybe one of my readers can shine some light on that?

Here is a complete sample of this method invocation technique outside of my MVC project:

class Program 
{
    static void Main(string[] args)
    {
        object str = "Hello World";
        object num = 5;
        object obj = new object();

        Console.WriteLine("var\tvalue\t\tFoo() Type\tCallFoo() Type");
        Console.WriteLine("-------------------------------------------------------");
        Console.WriteLine("{0}\t{1}\t{2}\t{3}", "str", str, MyClass.Foo(str), MyClass.CallFoo(str));
        Console.WriteLine("{0}\t{1}\t\t{2}\t{3}", "num", num, MyClass.Foo(num), MyClass.CallFoo(num));
        Console.WriteLine("{0}\t{1}\t{2}\t{3}", "obj", obj, MyClass.Foo(obj), MyClass.CallFoo(obj));
    }
}

class MyClass 
{
    public static Type Foo<T>(T param)
    {
        return typeof(T);
    }

    public static Type CallFoo(object param)
    {
        return (Type)typeof(MyClass).GetMethod("Foo").MakeGenericMethod(new[] { param.GetType() }).Invoke(null, new[] { param });
    }
}

Which, outputs:

var     value           Foo() Type      CallFoo() Type
   -------------------------------------------------------
   str     Hello World     System.Object   System.String
   num     5               System.Object   System.Int32
   obj     System.Object   System.Object   System.Object

Tuesday, July 28, 2009

Check for Content in Control

While working on a project recently I had a requirement for ContentPlaceHolder in my Master Page that was wrapped in a container that I only wanted to render if the ContentPlaceHolder was actually being populated by a page inheriting the Master. The container was a Firefox-style notice bar that renders at the top of the site with a simple message and a close button. The message area of the notice bar contains the ContentPlaceHolder which will govern whether or not the notice bar gets rendered. To achieve this two things must be done:
  1. Check if any controls exist in the ContentPlaceHolder's Controls collection
  2. If controls do exist, iterate over each one and check whether or not it:
    • A) Has a text property which is not whitespace
    • B) Contains controls in its own control collection meeting the latter condition
    • C) Is currently visible
These requirements are all relatively straightforward, and as you may have guessed, recursion comes to the rescue in this scenario. Although this code is ultimately going to be called on a ContentPlaceHolder, it makes more sense (and saves code) if the method accepts a non-specific control object, rather than a ContentPlaceHolder as its parameter.
public static bool IsNonEmptyControl(Control ctrl)
{
 if (!(ctrl is ITextControl) && ctrl.Controls.Count > 0)
 {
  for (int i = 0; i < ctrl.Controls.Count; i++)
  {
   if (IsNonEmptyControl(ctrl.Controls[i]))
    return true;
  }
 }
 else if (ctrl is ITextControl && ctrl.Visible)
 {
  return HasContent(ctrl as ITextControl);
 }
 return false;
}
This method is fairly simple and has one of two paths- immediate result, or recursion leading to a result. We start by checking if we're dealing with a container or a content control. This is a rather arbitrary test, but what this means is it's a control with a non-empty Controls collection or one with a "Text" property. If the control does have children we loop over each child and call IsNonEmptyControl() on it, or if the control has a text property we check the content of that text property with HasContent().
private static bool HasContent(ITextControl itc)
{
 if (itc != null && !string.IsNullOrEmpty(itc.Text) && !IsWhiteSpace(itc.Text))
  return true;
 //else
 return false;
}

private static bool IsWhiteSpace(string s)
{
 for (int i = 0; i < s.Length; i++)
 {
  if (!char.IsWhiteSpace(s[i]))
   return false;
 }
 return true;
}
Both of these methods are pretty obvious- check thatthe text property is not null, not empty, and does not contain whitespace ("\r, \n, " ", etc). And that's all there is to it. The method can be called on any control and will correctly indicate whether or not that control has content. In my case I call it in my Master Page's Page_PreRender event and pass it my ContentPlaceHolder. Now, there is one problem with this method: it doesn't properly address 2C from above- checking whether or not the content is actually visible when we call this from the Master Page on a ContentPlaceHolder. After digging a bit I realized this is actually less intuitive than I would have hoped due to your friend and mine: Page Life Cycle. When calling this method in the Master Page's PreRender event none of the content pages have yet initialized any of their controls from the code/markup so although they are accessible, all control properties are defaulted. In fact, it is not until Page_PreRenderComplete that we are able to see the actual property values of our controls and, unfortunately, the Master Page does not throw this event. I am honestly not sure how to handle this scenario and I would be very interested to know if anyone has some ideas on how to tackle this problem. Tips
  • If you're using C# 3.0 consider changing the signature to IsNonEmptyControl(this Control ctrl) for some Extension Method goodness.
Sources

Wednesday, July 8, 2009

ASP.NET Validation - Client-Side IsControlValid() Function

While working on a rather unique form for one of the websites I maintain at work (Rated Golf) I found myself needing to do some very case specific form validation on a collection of fields. All of the fields in question had a pair of ASP.NET validation controls attached to them and depending on what the client selected while filling out the form I needed to perform validation on a variable number of fields. Namely, the user could fill out either 9 or 18 of the fields in any order and provided that one of those conditions was met I would allow submission. I spent a good bit of time digging through the ASP.NET client-side validation API to get a feel for how to go about the task at hand and finally I decided on enumerating over the page's validator collection and making sure the correct number of fields checked out. I went to work on this approach only to soon find out there is no way to check if a given control is valid- only a given validator. Now I knew that each input control had exactly two validators and that would [most likely] never change so after some tooling around I slapped together a solution. The solution I came up with was not scalable when an input control had more than two validators, so after leaving work I came up with a slightly better, less specialized solution for this problem using some very basic JavaScript. I started by defining some simple ASP.NET markup to create a TextBox, a RequiredFieldValidator, and a CompareValidator.
<asp:TextBox ID="tbxValue" runat="server" ValidationGroup="vg1" Style="width: 400px;" />
<asp:RequiredFieldValidator ID="rfvValue" runat="server" ErrorMessage="Value is required."
ControlToValidate="tbxValue" Display="Dynamic" ValidationGroup="vg1" />
<asp:CompareValidator ID="cvValue" runat="server" ErrorMessage="Value must be a number."
ControlToValidate="tbxValue" Operator="DataTypeCheck" Type="Integer" Display="Dynamic"
ValidationGroup="vg2" />
Now that our page is setup we can jump into the JavaScript. The first place to start is figuring out how many validators exist for a given control. One of the convenient nuances of the Microsoft JavaScript framework is that input fields having validators are automatically decorated with a "Validators" array at run time, making it very simple to grab a list of validators for that control. In order to simplify working with the control and its validators after verifying its validity I chose to define an IsControlValidResult object to wrap the Control, its Validators, and an IsValid field. Of course this can easily be swapped out for a simple Boolean result if that better fits your implementation:
function IsControlValidResult() {
    this.IsValid = false;
    this.Control = null;
    this.Validators = new Array();
}
And finally:
function IsControlValid(controlId, validationGroup, displayErrors) {
    var result = new IsControlValidResult();
    var validatorCount = 0;
    var validCount = 0;
    if (controlId != null && typeof (controlId) == "string") {
        var control = document.getElementById(controlId);
        validatorCount = control.Validators.length;
        if (validatorCount > 0) {
            var i = validatorCount;
            while (i--) {
                if (validationGroup == null || IsValidationGroupMatch(control.Validators[i], validationGroup)) {
                    result.Validators.push(control.Validators[i]);
                    MyValidatatorValidate(control.Validators[i], validationGroup, null, displayErrors);
                    if (control.Validators[i].isvalid) {
                        validCount++
                        if (validCount >= validatorCount)
                            break;
                    }
                }
            }
        }
        result.Control = control;        
    }
    result.IsValid = validCount >= validatorCount;
    return result;
}
IsControlValid() function accepts a controlID string and an optional (read: nullable) validationGroup string in order to limit the validation to a given validationGroup for scenarios where more than one ValidationGroup is assigned to a control and its validators. The method is quite simple and works by iterating over the control's Validators array and for each validator it calls MyValidatorValidate(), which performs the actual validation. MyValidatorValidate() is identical to the framework function ValidatorValidate() aside from an additional Boolean variable controlling whether or not the error should be displayed graphically. I chose to do this because I wanted to check the validity of a validator without visually alerting the user that I had done so. Just be warned: even though MyValidatorValidate puts ValidatorUpdateDisplay() in a conditional statement, calling "val.evaluationfunction" will still set the state of the control as invalid and if you're using the AJAX Control Toolkit's "ValidatorCalloutExtender" it will render as soon as that function executes. I was not able to identify a workaround for this. Here's the code for MyValidatorValidate():
function MyValidatatorValidate(val, validationGroup, event, displayError) {
    val.isvalid = true;
    if ((typeof (val.enabled) == "undefined" || val.enabled != false) && IsValidationGroupMatch(val, validationGroup)) {
        if (typeof (val.evaluationfunction) == "function") {
            val.isvalid = val.evaluationfunction(val);
            if (displayError && (!val.isvalid && Page_InvalidControlToBeFocused == null && typeof (val.focusOnError) == "string" && val.focusOnError == "t")) {
                ValidatorSetFocus(val, event);
            }
        }
    }
    if (displayError)
        ValidatorUpdateDisplay(val);
}
If, after calling MyValidatorValidate, you have any ValidatorCalloutExtenders being rendered on the page that you would like hidden from the user, the following function will turn off all visual validation cues:
function Unvalidate(myValidationGroup) {
    // Remove the validator control(s) from display.
    var myValidators = Page_Validators;
    if ((typeof (myValidators) != "undefined") && (myValidators != null)) {
        for (i = 0; i < myValidators.length; i++) {
            var myValidator = myValidators[i];
            if (myValidationGroup == null || IsValidationGroupMatch(myValidator, myValidationGroup)) {
                if (myValidator.style.visibility.length > 0 && myValidator.style.display.length == 0) {
                    myValidator.style.visibility = 'hidden';
                }
                else if (myValidator.style.display.length > 0 && myValidator.style.visibility.length == 0) {
                    myValidator.style.display = 'none';
                }
                if ((typeof (myValidator.ValidatorCalloutBehavior) != "undefined") && (myValidator.ValidatorCalloutBehavior != null)) {
                    myValidator.ValidatorCalloutBehavior.hide();
                    if ((typeof (myValidator.ValidatorCalloutBehavior._highlightCssClass) != "undefined") && (myValidator.ValidatorCalloutBehavior._highlightCssClass != null) && (typeof (myValidator.ValidatorCalloutBehavior._elementToValidate) != "undefined") && (myValidator.ValidatorCalloutBehavior._elementToValidate != null))
                        Sys.UI.DomElement.removeCssClass(myValidator.ValidatorCalloutBehavior._elementToValidate, myValidator.ValidatorCalloutBehavior._highlightCssClass);
                }
            }
        }
    }

    // Remove the validator summary(ies) from display.
    if ((typeof (Page_ValidationSummaries) != "undefined") && (Page_ValidationSummaries != null)) {
        for (i = 0; i < Page_ValidationSummaries.length; i++) {
            var mySummary = Page_ValidationSummaries[i];
            if (myValidationGroup == null || IsValidationGroupMatch(mySummary, myValidationGroup)) {
                mySummary.style.display = 'none';
            }
        }
    }
}
And that's all there is to it. If you found this useful don't hesitate to let me know in the Comments!

Visual Studio 2008 var Keyword ‘Bug’ – Microsoft Follow-up

I got a response back from Microsoft support regarding an issue with the var keyword in Visual Studio 2008 and what they had to say isn’t particularly surprising (or motivating):

Thanks for the feedback! You're seeing that using C# 3.0 specific language features in a .NET 2.0 targeted web project fails since the code is actually being compiled on the server using the C# 2.0 compiler (more specifically, the script blocks of Web App Projects and all of Web Sites). This is a limitation that we're currently aware of and one that we will be re-evaluating as we begin planning for the next release. In the meantime, when targeting .NET 2.0, you can actually go into "Project | Properties | Build | Advanced Build Settings" and set the language version of the project to be ISO-2, which will force the design time compilers to emit errors for C# 3.0 specific language features.

Given that we won't be able to address this issue in the upcoming VS2010 release, I'm going to go ahead and mark this bug as a "Wont Fix" but please be assured that we are tracking this suggestion internally and will be considering it again for the next product cycle.

Thanks!
DJ Park
C# IDE, Program Manager

I tested the ISO suggestion the report handler sent me and as far as I could tell ISO-2 does not cause Visual Studio to emit any errors when the var keyword is used. ISO-1 did produce some non-standard build errors, but nothing to do with the var keyword. If anyone can demonstrate otherwise I’d love to hear about it in the comments.

It would have been foolish to think Microsoft would actually want to fix this little issue, but at least they know it’s there.

Thursday, July 2, 2009

Visual Studio 2008 var Keyword ‘Bug’

I stumbled across an interesting bug while editing an ASP.NET web project in Visual Studio 2008 a few days ago and I figure after four years of using the Visual Studio suite without encountering any real bugs, one isn't all that bad. That being said, given the nature of the behavior I encountered, I was compelled to investigate the issue behind it.

With the introduction of C# 3.0's implicit variable keyword var Microsoft added some simple tricks to the compiler in order to produce the traditional C# 2.0 IL code backing this new keyword. That being so, .NET 2.0 projects are fully capable of running projects written with most C# 3.0 syntax.

The bug I encountered, if you can call it that, pertains to how Visual Studio treats code written inside ASP script tags within the page markup. As has been the case since classic ASP, C# or VB code can be written directly into the markup using <% %> tags. Consider the following rudimentary example:

<%
  string hello = "Hello";
  Response.Write(hello);
%>

Like code placed in your code behind, this code is run when the page is loaded by the user; unlike the code in your code behind, this code is not compiled until runtime. A key distinction.

This brings me to the bug I noticed in the editor that pertains more to syntax highlighting than anything else. Consider this identical example written with C# 3.0 syntax.

<%
  var hello = "Hello";
  Response.Write(hello);
%>

Functionally these two blocks of code perform the same task and produce the same IL code when compiled; however, running the latter piece of code in a .NET 3.5 project will not yield the same results as it does in a .NET 2.0 project. Due to the fact that code contained in script tags is not compiled until runtime the target environment ultimately affects the compilation of that code. For that reason, given that the .NET 2.0 runtime doesn’t know what var means it cannot correctly detect the type and compile the code.

Now this behavior is neither surprising nor a bug in itself, but rather it pertains to small lack of gracefulness on Visual Studio’s part in detecting this specific condition. In Visual Studio 2008 while editing a C# project var is a keyword that is highlighted by the editor regardless of the target platform because in all but this one scenario var can safely be used in a .NET 2.0 project and, unfortunately, the development team overlooked this one scenario.

One more thing worth noting for all you ReSharper users out there is the breaking vulnerability this exposes with the Code Cleanup tool in the plug-in. If you have ever tried configuring the Code Cleanup options you may have noticed it is possible to tell it to replace all explicit variable definitions with the implicit var keyword. If you’re running a .NET 2.0 project with inline script tags in your pages make sure to watch for this setting and ignore the inline code inspection advice.

Happy Coding!