Monday 7 September 2015

Strange "method not found". Or common sense?

Imagine a situation. You have a big solution and it is already deployed. Then one day, you get to hear about an issue with one of the function implementation in one of the DLLs which required you to change type of a property. The change is not as drastic as changing an integer to string or vice versa. Rather it requires you to change type of a property from integer to double or double to integer.

We will consider change from integer to double for now. The solution compiles just fine and it looks like you are good to deploy the modified dll on running environment as there is no change in the other components that use this property. What happens next :)? For illustration, I created a Class Library named "ClassLibrary1" which has a class "Class1".



public class Class1

    {

        public int MyProperty { get; set; }

    }
I have another component "NewConsoleApplication" which has following method:


class Program

    {

        static void Main(string[] args)

        {

            double doubleValue = 0.4f;

            var c = new ClassLibrary1.Class1() { MyProperty = 100 };

            Console.WriteLine(doubleValue * c.MyProperty);

        }

}

It works just fine. Now you can change the type of property "MyProperty" to double and drop the compiled dll in the bin of "NewConsoleApplication". If you run the exe, you start to encounter weird error:

Method not found. Void set_MyProperty (Int32).

But you already changed the DLL. There is no property of type integer anymore. Why is someone trying to look for integer type?

The reason is simple. The IL code generated in NewConsoleApplication assumes that the property is still Integer and has its own IL code generated like that e.g. the code required to multiply casts the MyProperty to double before multiplying.


double num = 0.40000000596046448;
    Class1 @class = new Class1();
    @class.set_MyProperty(100);
    Class1 class2 = @class;
    Console.WriteLine(num * (double)class2.get_MyProperty());

So, there is the mystery.

Lesson: if there is a type change in one component, calling assemblies should be regenerated and replaced even if they have no change in their own code.

Reading encrypted content in external configuration file

.NET framework has a great configuration feature - external configuration files/external configuration source. It allows you to move certain configuration sections or parts of appSettings to an external file and then mention the path of external configuration file in the original configuration file. .NET framework merges the two configuration files and its ConfigurationManager API works seamlessly. Till you encrypt the external configuration file :).
 
So what is the solution? Custom code, anyone? Under the hood, .NET framework uses DpapiProtectedConfigurationProvider to some extent to read/write encrypted data. We can use the same along with ConfigXmlDocument. Here is one function that I wrote to read appSettings value from external configuration file that may or may not be encrypted:
 
public static class CommonConfigurationProvider
    {
        public static string GetAppSettingsValue(string key, string configurationFilePath)
        {
            ConfigXmlDocument configXmlDocument = new ConfigXmlDocument();
            configXmlDocument.Load(configurationFilePath);
 
            XmlNode appSettingsNode = null;
            if (configXmlDocument.DocumentElement.LocalName == "appSettings")
            {
                appSettingsNode = configXmlDocument.DocumentElement;
            }
            else
            {
                appSettingsNode = configXmlDocument.DocumentElement.SelectSingleNode("appSettings");
            }
 
            if (appSettingsNode == null)
            {
                throw new Exception("No appSettings section found!!");
            }
 
            XmlNode settingNode = null;
            if (appSettingsNode.Attributes["configProtectionProvider"] != null)
            {
                DpapiProtectedConfigurationProvider p = new DpapiProtectedConfigurationProvider();
                settingNode = p.Decrypt(appSettingsNode.SelectSingleNode("EncryptedData"));
            }
            else
            {
                settingNode = appSettingsNode;
            }
 
            if (settingNode != null)
            {
                var valueNode = settingNode.SelectNodes("add");
                if (valueNode != null)
                {
                    for (int i = 0; i < valueNode.Count; i++)
                    {
                        if (valueNode[i] != null && valueNode[i].Attributes["key"].InnerText == key)
                        {
                            return valueNode[i].Attributes["value"].InnerText;
                        }
                    }
                }
            }
 
            return null;
        }
    }
 
 
You can use both relative or absolute file path. Using this method is quite straightforward.
 
Console.WriteLine(ConfigurationManager.AppSettings["TestKey1"]);
            Console.WriteLine(CommonConfigurationProvider.GetAppSettingsValue("TestKey2", @"ExternalConfig\app.config"));
            Console.WriteLine(CommonConfigurationProvider.GetAppSettingsValue("TestKey3", @"ExternalConfig\app.config"));
The only caveat that I found was that it did not behave seamlessly. Of course, we can write a wrapper on ConfigurationManager class that takes care of that and makes usage seamless. Shouldn't be that hard.