Thursday, September 23, 2010

Covariance & Contravariance Examples

I recently held a discussion on this topic and put together some sample code for demonstration purposes. Here it is if anyone is interested. It goes along with Part 1 of the Covariance & Contravariance discussion.

Monday, September 20, 2010

Covariance & Contravariance - Part 1

One of the new features of C# 4.0 is support Covariance and Contravariance. The most basic example of the concept is pretty straightforward to grasp, but after further inspection, you'll find it can be tricky. In short, the concept describes how to determine equivalence between types. There are only 3 cases to consider when determining if two types are equivalent. Given two types S and T:
  1. S is more general than T.
  2. S is more specific than T. 
  3. Neither case 1 nor 2.

Covariance
You already know from experience that in case 1, you can assign an instance of type T to a variable of type S. The reason you can do this is because the compiler assumes that because S is a superclass (more general) than T, then a variable of type S can surely point to an instance of type T. This type of determination by the compiler is called Covariant

Covariance has actually been around since .NET 1.0, we just didn't realize it. Take the following example:
Employee[] employees = new Employee[10];
Person[] people = employees;
 


Array assignments in C# have always been assumed to be covariant. When C# 2.0 came around, they added another kind of implicit covariance. Take a look at the following:

delegate TOut GetItem<TOut>();

Programmer GetProgrammer()
{
    return new Programmer();
}

GetItem<Employee> getEmployee = GetProgrammer;
 

This should make sense because if I execute getEmployee, the result would still be an instance of type Employee since a Programmer is an Employee. The equivalence operation is going from specific to general, which is known as covariance.



Contravariance
Following from our previous example, if we take our types and instead of making them be the return type, we make them the parameter type, we get the following:
delegate void PutItem<TIn>(TIn item);

void PutProgrammer(Programmer item) { }

PutItem<Employee> putItem = PutProgrammer;
 


All we've done is move the types from being a return value, to a parameter value. If you were to try this, it would be illegal in C# because you could potentially pass in an instance of ProductManager into the putItem method reference; which would mean you're passing in a ProductManager instance into the PutProgrammer method which expects a Programmer. So if the PutProgrammer method were accessing a Programmer specific property such as KnownProgrammingLanguages, it would fail because that property doesn't exist on a ProductManager. In order to make this legal, we'd have to reverse the types in the assignment operation on line 5, and the type in the method on line 3:
delegate void PutItem<TIn>(TIn item);

void PutEmployee(Employee item) { }

PutItem<Programmer> putItem = PutEmployee;
 

Now this is valid because I can only put a Programmer instance into the putItem method, the PutEmployee method that putItem points to will be able to accept the Programer instance since a Programmer is an Employee. Notice now that the operation on line 5 is now reversed. This is an example of case 2 because the equivalence operation on line 5 is now going from general to specific. This is known as contravariance, which (surprise!) is the reverse of covariance. 


Naturally, methods are covariant in their return types, and contravariant in their parameter types. This is not always the case, but is an important observation.


In Covariance & Contravariance - Part 2, I'll go into how this seemingly meaningless knowledge can be applied to C# 4.0.

Wednesday, September 15, 2010

Visual Studio Tips

I recently attended a class led by John Robbins on .NET Debugging which discussed how to use the various debugging tools in Visual Studio. There were a few ticks I found interesting that I thought I'd share.


Quickly Find a File and Open it
If you are like me and have solutions and projects that contain lots of files, it can sometimes be a trick to remember where that one particular file is. You can't remember the exact name, but know what it starts with. Pressing CTRL+D (the default hot key for Edit.GoToFindCombo) and then typing >open followed by the partial name of the file will bring down the list of files. You can then select the file of interest and press Enter to open it.


Placing a Breakpoint in a Function
Sometimes you know the name of a function that you want to place a breakpoint in, but don't have the file open. A quick way to put a breakpoint at the function is to press CTRL-D (the default hot key for Edit.GoToFindCombo), type the name of the function, and press  F9. This places a break point on the first line of the specified function. This is pretty useful if you are looking at a log file with a stack trace and want to jump straight to a particular function and debug.


Seeing the Current Exception
There are times when you catch an exception but don't care about the actual exception instance.


try
{
}
catch(InvalidOperationException)
{
    // Do something special, then throw
    throw;
}
When you're debugging, you might need the actual instance to dig deeper into the cause. If you put $exception into your watch window, you 'll see it.

Friday, September 10, 2010

Single Sign-on for Cross Domain Applications

In general, web authentication puts a cookie in your browser that says, "I've authenticated with you, and I am who I say I am." This is works great until you have parts of your application that exist on another domain; or even multiple applications for that matter. The system breaks down in this scenario because a cookie put into the browser is only accessible to the domain (and sub domains) that created it.

For example, if you have www.MyServiceA.com and you login there, it puts a cookie into your browser that says you've authenticated. When you visit other parts of www.MyServiceA.com, they see the cookie and give you access. If www.MyServiceA.com links you to www.MyServiceB.com, the latter site will not have access to the cookie created for www.MyServiceA.com and will force you to login again.

The Basic Approach
One method of doing this requires several redirects and passing the cookie info in a URL string.


At step 1, the user logs into mail.com/login and gets the authentication token xyz. The user then clicks a link that takes them to calander.com/default (step 2), which sends back a redirect to a special page on mail.com that gets the authentication cookie and sends it back in the URL's query string (step 3). The redirect from step 3 sends the browser to a special page on calander.com that looks at the query string to determine the cookie value and sets the cookie in the browser accordingly (step 4), redirecting the user back to the original page at calander.com requested in step 2.

A Simpler Approach
This solution works, but when I first saw it I thought there had to be a better way. Upon further research, I found that you can actually simply things by combining steps 3 and 4 into an HTML img tag. The silence and blank expression on your face tell me you need more explanation.

The trick, is to include an image tag that has its source set to the special setCookie URL at calander.com, passing the current authentication token in the query string part of the URL. This way when the browser renders the image on the mail.com page, it will hit the URL (which has the authentication token in its query string), which will be giving the cookie value to the other domain (calander.com). The other domain can then take the cookie value and tell the browser to set that cookie for the calander.com domain. Now when the user navigates to calander.com the cookie will be available and the user can be considered authenticated.

<html>
    <head>
    </head>
    <body>
        <script src="scripts/jquery-1.3.2.min.js" type="text/javascript"></script>
        <script type="text/javascript">
            $(document).ready(function () {
                var singleSignOnUrl = "<%= this.SingleSignOnUrl %>?c=<%= this.SingleSignOnToken %>";

                //
                // Add an image element with the source set to the special cookie
                // setter URL. Since the URL is not responding with a valid image,
                // the element's onload event will not fire. Instead, the onerror
                // event will be fired after the special URL finishing processing.
                //
                $("#img").attr("src", singleSignOnUrl));
            });

            function onError() {
                    $("#lblMessage").text("Cookie in the other domain has been set!");
                    // Make link to calander.com available
            }
        </script>        
  <img id="img" style="display: none;"></img>
  <span id="lblMessage">Setting cookie...</span>
    </body>
</html>

The example above assumes you have an aspx page with SingleSignOnUrl and SingleSignOnToken properties on the page that return the special calander.com/setCookie and authentication cookie value respectively. The setCookie page could be an http handler (setCookie.ashx) that does something like this:

///////////////////////////////////////////////////////////////////////////
    /// 
    /// Handler that looks for a specific query string parameter and reflects
    /// it back as a cookie.
    /// 
    ///////////////////////////////////////////////////////////////////////////
    public class SingleSignOnHttpHandler : IHttpHandler
    {
        ///////////////////////////////////////////////////////////////////////////
        /// Key used to represent the single sign on token.
        ///////////////////////////////////////////////////////////////////////////
        public const string AuthenticationTokenKey = "authentication-token";

        ///////////////////////////////////////////////////////////////////////////
        /// 
        /// Initializes a new instance of the  class.
        /// 
        ///////////////////////////////////////////////////////////////////////////
        public SingleSignOnHttpHandler()
        {
        }

        #region IHttpHandler Members

        ///////////////////////////////////////////////////////////////////////////
        /// 
        /// Gets a value indicating whether another request can use the  instance.
        /// 
        /// 
        /// true if the  instance is reusable; otherwise, false.
        /// 
        ///////////////////////////////////////////////////////////////////////////
        public bool IsReusable
        {
            get { return true; }
        }

        ///////////////////////////////////////////////////////////////////////////
        /// 
        /// Enables processing of HTTP Web requests by a custom HttpHandler that implements the  interface.
        /// 
        /// An  object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.        ///////////////////////////////////////////////////////////////////////////
        public void ProcessRequest(HttpContext context)
        {
            //
            // Take the appropriate query string parameter and reflect it back as a cookie.
            //
            context.Response.ContentType = "text";
            context.Response.AddHeader("p3p", @"CP=""NON ADM OUR""");

            string value = context.Request.QueryString["c"];
            if (value.IsValid())
            {
                context.Response.SetCookie(new HttpCookie(AuthenticationTokenKey, value));
            }
        }

        #endregion
    }

The p3p header information is necessary for compatibility with Internet Explorer. Without that header, IE would ignore the returned cookie.

At this point, once the user accesses the page at mail.com with the hidden image, the users's browser will have the authentication cookie set in both the mail.com and calander.com domains, which will allow them to navigate between the two domains without having to authenticate.