Thursday, February 21, 2013

When the new keyword met reflection

The New Keyword

Consider the following code:

    public class BaseClass
    {
        public int? TheProperty { get; set; }
    }

    public class DerivedClass : BaseClass
    {
        public new int TheProperty
        {
            get { return base.TheProperty ?? 0; }
            set { base.TheProperty = value; }
        }
    }

The new keyword is used in order to hide a property or method of the base class. It can be used in order to give another implementation for the property even if the property is not defined as virtual in the base class. As it hides the property of the base class it can even have a different type. In this example, the property of the base class is of type int? while the property of the derived class is of type int.
In the following code the property of the base class is called:

            BaseClass instance = new DerivedClass();
            instance.TheProperty = null;

While in the following code the property of the derived class is called:

            DerivedClass instance = new DerivedClass();
            instance.TheProperty = 5;

The Problem

The following method is using reflection in order to get the value of a property:

        public static object GetPropertyValue(object obj, string propertyName)
        {
            System.Reflection.PropertyInfo propertyInfo = obj.GetType().GetProperty(propertyName);
            object value = propertyInfo.GetValue(obj, null);
            return value;
        }

This method generally works fine, but when obj is an instance of DerivedClass (and propertyName is "TheProperty"), the first line of the method will throw the following exception: "Ambiguous match found.". That is because the DerivedClass does not have a single property called TheProperty, but rather two different properties of different types by that name.

Possible Solutions

First we should consider dropping the new keyword. Hiding a property is not an obvious thing to do, so first I would review why hiding was used in the first place. As we seen in this post hiding can be a little confusing so I think it should be considered carefully.

If we do decide to keep hiding the property, we can modify the GetPropertyValue method to support it by using the following code:
        
        public static object GetPropertyValue(object obj, string propertyName)
        {
            System.Reflection.PropertyInfo propertyInfo = 
                obj.GetType().GetProperties().FirstOrDefault(prop => prop.Name == propertyName);
            object value = propertyInfo.GetValue(obj, null);
            return value;
        }

If you are looking specifically for any one of the properties you can update the predicate used by FirstOrDefault to consider DeclaringType (for example, prop.DeclaringType == typeof(DerivedClass)) or PropertyType (for example, prop.PropertyType == typeof(int?)).

In our case we decided to modify the GetPropertyValue as it is part of our infrastructure, and we decided we would rather have our infrastructure support such cases.

Saturday, January 12, 2013

The Session Story

Prologue

Programming in ASP.NET we use the Session object in order to keep user information on the server while the user is working with the application. That way you can easily access that information when the server is working and require that information. Usually we use the session on the main ASP.NET thread. What would happen if you use the session in another thread? Can you update the session while on another thread? So here is the story.

The Feature

We developed a very cool feature which enables users to upload their PDF files, and then view and manipulate them before completing an order for printing the composite document. When a user uploads a file we have to analyse it and turn its pages into images (in order to display them in the browser). This processing takes time. The user will have to wait, but we do not want the web server to wait, taking over application pool threads, but rather do this processing in separate threads.

The Problem

When the processing of a file completes we need to update the session object with the newly uploaded file and its properties, so we could handle the order on the server side. At first, we did this update on the same thread that did the processing. When we use In Proc session state mode it worked well, but when we use SQL Server session state mode (required for clustering), the session will not update.

Why?

We wondered why does it work that way. At first it felt very strange and we said it's probably some bug. But we also felt that it may be that we are misusing the session, so we did some more research and thinking, and this is what we came up with.
In Proc session state mode simply keeps the session in memory. When we update the session from one thread and then read it from the main thread, they both access the same place in memory, so it works.
SQL Server session state mode needs to serialize the session to the database in order to save it, and deserialize it in order to read it. Deserialization occurs when a request hits the server, and serialization occurs when the request ends and the response goes to the client.
When updating the session in another thread, it happens after the request ends, and it also occurs after the session was serialized and saved in the database, so this explains why it cannot work that way.
Doing some further reading, I found out that the session was designed to work only in the scope of a request, so also in the case of using only In Proc session state mode, updating the session from another thread after the request is over is an abuse and breaks the session design.

The Solution

We decided to simply change our design a bit, so the request to upload a file, which triggers its processing and should have saved it to the session, will not do the saving. Instead, as the client waits for processing to end by asking the server whether processing is over, when the server finds out processing is done, it saves the file information to the session, and returns the correct response to the client. That's easy to implement and easy to understand.

Epilogue

So far I was only telling stories on this blog. I hope to put some code into my next post, so keep on following!