Wednesday, April 23, 2014

Access 2013: Protecting the Programmatic Elements of a Database

This posting presents ways to protect an Access database so that users cannot edit the underlying database. This means a user should not be able to edit the VBA code, reports and forms of an Access database. The user should be able to run the database. Additionally, the user should not be able to edit the underlying tables.

Convert Database to Execute-Only Mode


The most straight forward way to project an Access database (*.accdb file) is to save it as an executable, namely a file with an accde extension. This is achieved by selecting Save As from access and saving the *.accdb file with an *.accde extension as follows. An example of Access 2013's Save As dialog is as follows:




Once the accde file type has been select, click on the Save As button.

The standard file extension (referred to as an Access 2007 file type) is *.accdb. This extension replaced the *.mdb extension used by per-Access 2007 versions of Access. The *.accde extension is also a valid Access 2007 extension. When a file is saved as *.accde, the file is in execute only mode. An Access database in execute only mode the following behavior:

  • The VBA code is compiled and not included in the distributed database
  • Forms cannot be modified
  • Reports cannot be modified.

Even though the Access database is converted into an executable (*.accde extension), a copy of Access is still required to run the file.

Access Runtime

It is possible to distribute an Access application without requiring Access to be physically installed. From a cost stand-point this is a tremendous savings. It also means that user cannot directly access the tables and queries of the underlying database, since Access is not installed. This does not prevent them from installing Access.

Executing an Access application without requiring Microsoft Access is achieved using the Microsoft Access Runtime found at: Microsoft Access 2010 Runtime. The Access Runtime can run database with either an *.accdb or *.accde extension.

The runtime means that the application will but a user will not have access to the design related toolsbut the Access project will execute.

Runtime Behavior on a Machine on which Access is Installed


It is possible to determine how an application behaves when running under the Access Runtime even if Accss is installed. The Access application (MSAccess.exe) supports a command line option, /Runtime. specifying the /Runtime options means Microsoft Access will run an Access application as if the application were running under the Access Runtime.

For example using full Microsoft Access 2013 it would be possible to run fun.accdb as if it was running under the Access Runtime using the following command:

"C:\Program Files\Microsoft Office 15\root\office15\MSACCESS.EXE" fun.accdb /Runtime

It would also be possible to run an Access executable such as fun.accde as if it was running under the Access Runtime using the following command:

"C:\Program Files\Microsoft Office 15\root\office15\MSACCESS.EXE" fun.accde /Runtime

Monday, April 21, 2014

Upgrading your MLB subscription to include minor league games for $25 a year

I write many of these posts to save people time. This post has nothing to do with technology. I am a yearly subscriber to MLB.tv Premium (access to all Major League Baseball games for $129.99). For $25 a year you can add minor league games to your MLB.tv. Minor league games are MiLB.tv and normally costs $49.99 for a yearly subscription. The problem is there is no way to upgrade an existing MLB.tv subscription to add the $25 MilLB.tv subscription.

 After thirty minutes on the phone, customer service pointed me to this URL: www.milb.tv/yearlydiscount. This link allows you to buy MiLB.tv for half price.

Sunday, April 20, 2014

C#: Encryption (studying for Exam 70-483; Programming in C#)

Introduction

This blog posting reviews the types of encryption supported in .NET. This includes:
  1. File Encryption
  2. Windows Data Protection
  3. Hashing
  4. Symmetric Encryption
  5. Asymmetric Encryption 

This review is spawned because it is an area of development I have worked in sparingly. This is the knowledge that I need. I started developing in C# during Alpha 1 of .NET in 2000. Fourteen years later I am finally getting around to taking the "Exam 70-483; Programming in C#" certification example. You'd think I know it all by now. In preparation I skimmed an excellent course (I only skimmed because I already knew so much of the material) provided by Microsoft Virtual Academy. The course was "Programming in C# Jump Start".

It should be noted that .NET does not implement encryption and decryption. .NET takes advantage of Windows native encryption. This means that the encryption exposed by .NET is extremely fast because it is operating system supported.

Strings, UTF-16 and UTF-8

.NET strings are UNICODE and contain two bytes per-character -- UTF-16. Encryption is performed at the byte level. When text (the .NET String class) is encrypted, the String is converted to UTF-8 which means each character is represented by between one to four eight bit bytes. The System.Text namespace provides classes that can convert between encodings. When data is hashed or encrypted the following will be used to convert from String to byte array using:
const string data =
    "If we are mark’d to die, we are enow. " +
    "To do our country loss; and if to live, " +
    "The fewer men, the greater share of honour.";
byte[] dataAsBytes = System.Text.Encoding.UTF8.GetBytes(data);

In the previous code snipped the Encoding class is used to get the UTF-8 byte representation of the string to be processed.

FYI: If you do not understand why the terminology "eight bit bytes" is used look up UTF-7, an encoding where bytes have seven bits.

File Encryption

Developers recognize that the key to working with files, folders and paths is the System.IO namespace and the File, Directory and Path static classes. These classes date back to .NET 1.0. As of .NET 2.0  two methods were added to the File class related to the encryption and decryption of files:
  • Encrypt: Encrypts a file so that only the account used to encrypt the file can decrypt it.
public static void Encrypt(string path)
  • Decrypt: Decrypts a file that was encrypted by the current account using the Encrypt method.


public static void Decrypt(string path)

It should be clear from the above documentation the ability to decrypt a file is tied to the user credentials used to encrypt the file.

An example program that both encrypts and decrypts a text file containing part of Shakespeare's "Henry V" Saint Crispin's day speech is as follows:

using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Collections.Generic;

namespace FileEncryption
{
  class Program
  {
    static void Main(string[] args)
    {
      const string fileBody = 
          "If we are mark’d to die, we are enow. " + 
          "To do our country loss; and if to live, " +
          "The fewer men, the greater share of honour.";
      const string filename = "FileToDecrypt.txt";
      var fullyQualifiedFilename = 
         Path.Combine(Path.GetTempPath(), filename);

      // Create the file and encrypt the file
      File.WriteAllText(fullyQualifiedFilename, fileBody);
      File.Encrypt(fullyQualifiedFilename);

      // Decrypt the file
      File.Decrypt(fullyQualifiedFilename);   

      var currentBody = File.ReadAllText(fullyQualifiedFilename);
      Trace.Assert(fileBody == currentBody);
    }
  }
}

Encrypting a file with File Explorer

Remember the encryption exposed by .NET is simply the encryption implemented by the operator system. To encrypt a file within File Explorer right block on the file and select properties and then select the General tab: 

From the General tab click on the Advanced button:


Click on the "Encrypt contents to secure data" check box combined with clicking on OK, encrypts the file. When a file is encrypted the name of the file within File Explorer lightens in color. After Bing-ing this, I learned the color was "green" (for the majority who see colors -- encrypted files are green).

Windows Data Protection

Data of type byte array (byte[]) is what Windows Data Protection encrypts and decrypts. Like file encryption, Windows Data Protection ties the encryption/decryption process to a specific set of user credentials. As a technology Windows Data Protection was made available as of Windows 2000 and later (great since that was well over a decade ago). At the operating system level, Windows exposes the Data Protection API (DPAPI).

The .NET class used to access DPAPI is ProtectedData found in the System.Security.Cryptography namespace. The assembly for ProtectData is System.Security.dll. The ProtectedData class is a static class that exposes the following methods:
  • Protect: encrypts the data in a specified byte array and returns a byte array that contains the encrypted data.
  • Unprotect: decrypts the data in a specified byte array and returns a byte array that contains the decrypted data.
The signature for Project is as follows:

public static byte[] Protect(
byte[] userData,
byte[] optionalEntropy,
DataProtectionScope scope)

The first parameter, userData, is the byte array of data to be encrypted. The second parameter, optionalEntropy, is optional hence it can be specified as null. The optionalEntropy parameter increases the complexity of the encryption used. The more byte specified via the parameter, the better the encryption.

The values for DataProtectionScope are 
  • CurrentUser: only the current user can use Unprotect or the underlying DPAPI to decrypt the data.
  • LocalMachine: any process on the machine can use Unprotect or the underlying DPAPI to decrypt the data. This ability for any process to decrypt is because the file is encrypted using the machine's context.
The signature for Unproject is as follows:

public static byte[] Unprotect(
byte[] encryptedData,
byte[] optionalEntropy,
DataProtectionScope scope)

The first parameter, encryptedData, is the byte array previously encrypted by Protect. The values for the optionalEntropy and scope must match the values specified when Protect was invoked to originally encrypt the data.

When developing an application that makes use of ProtectedData the System.Security.dll assembly must be reference by the project.

An example program that both protects (encrypts) and un-protects (decrypts) a byte array containing part of Shakespeare's "Henry V" Saint Crispin's day speech is as follows:

using System;
using System.IO;
using System.Linq;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Security.Cryptography;

namespace WindowsDataProtection
{
  class Program
  {
    static void Main(string[] args)
    {
      const string data = 
          "If we are mark’d to die, we are enow. " + 
          "To do our country loss; and if to live, " +
          "The fewer men, the greater share of honour.";
      byte[] dataAsBytes = 
          System.Text.Encoding.UTF8.GetBytes(data);
      byte[] makeItMoreComplex = { 1, 3, 2, 4, 5, 7, 6, 8 };

      var backToString = 
          System.Text.Encoding.Unicode.GetString(dataAsBytes);

      var thisIsEncyrpted = ProtectedData.Protect(
            dataAsBytes, 
            makeItMoreComplex, 
            DataProtectionScope.CurrentUser);
      // The string created here is garbage -- encrypted bytes 
      // converted to text
      var backToStringMangled = 
         System.Text.Encoding.Unicode.GetString(thisIsEncyrpted);

      var thisIsDecyrpted = ProtectedData.Unprotect(
            thisIsEncyrpted, 
            makeItMoreComplex, 
            DataProtectionScope.CurrentUser);
      var backToStringCorrect = 
         System.Text.Encoding.UTF8.GetString(thisIsDecyrpted);
      Trace.Assert(data == backToStringCorrect);
    }
  }
}

Hashing

A hash is signature computed on a sequence of data. The signature is designed to detect if the data has been modified which often means to detect if the data has been corrupted. Two pieces of data may generate an identical hash (which should be unlikely given a complex enough hashing algorithm). It is unlikely that a piece of data that has been modified will generate identical hashes both before and after modification. 

The way a great many of us learned about real-world hashing was with downloading an ISO (DVD image) or EXE from MSDN. The EXE and ISO files were often hundreds of megabytes and a corruption means the ISO could not be mounted or the EXE could not be run. To detect if there was a corruption each ISO/EXE is hashed using SHA1. The following screenshot is from the MSDN download page where the download is 2865 mega bytes in size and the SHA1 hash computed on the ISO downloaded is provided directly on the page:


The value for the SHA1 hash is  41843D4B92CF85008715860031AB6F2FACEC5373 for the reference ISO.

The support for hashing in .NET is provided by the System.Security.Cryptography namespace. There are different possible hashing algorithms (MD5, SHA1, etc.). Each of these algorithms is implemented using the HashAlgorithm as a base class. The standard list of providers derived from the HashAlgorithm base class is as follows:

System.Object 
  System.Security.Cryptography.HashAlgorithm
    System.Security.Cryptography.KeyedHashAlgorithm
    System.Security.Cryptography.MD5
    System.Security.Cryptography.RIPEMD160
    System.Security.Cryptography.SHA1
    System.Security.Cryptography.SHA256
    System.Security.Cryptography.SHA384
    System.Security.Cryptography.SHA512

The following code demonstrates how to compute a hash using SHA512 and how to compare hashes:

using System;
using System.IO;
using System.Linq;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Security.Cryptography;

namespace Hashing
{
  class Program
  {
    static void Main(string[] args)
    {
      const string data =
          "If we are mark’d to die, we are enow. " +
          "To do our country loss; and if to live, " +
          "The fewer men, the greater share of honour.";
      byte[] dataAsBytes = 
          System.Text.Encoding.UTF8.GetBytes(data);
      var hasher = SHA512.Create();
      byte [] hashOriginal = hasher.ComputeHash(dataAsBytes);

      // String is invariant to use a StringBuilder so 
      // that the text can be modified
      var dataToMangle = new System.Text.StringBuilder(data);

      dataToMangle.Replace("country", "city");

      var mangledData = dataToMangle.ToString();
      byte[] mangledDataAsBytes = 
          System.Text.Encoding.UTF8.GetBytes(mangledData);
      byte[] hashMangled = 
                  hasher.ComputeHash(mangledDataAsBytes);

      Trace.Assert(Enumerable.SequenceEqual(
                       hashOriginal, hashMangled) == false);
    }
  }
}

The comparison between the hash byte arrays is performed by Enumerable.SequenceEqual (available .NET 3.5 and later). This static method is documented within MSDN to: Determine whether two sequences are equal by comparing the elements by using the default equality comparer for their type.

The full documentation for SequenceEqual can be found in MSDN as follows:



Microsoft's knowledge base has an excellent article for developing hash values using C#: How to compute and compare hash values by using Visual C#.

Symmetric Encryption

With regard to a definition of symmetric and asymmetric encryption, Microsoft has a knowledge base article: Description of Symmetric and Asymmetric Encryption. Symmetric encryption is where the encrypting application and the decrypting have the same key. If a client encrypts with the password, MorrisseyMarr then passes the encrypted data to a server, the server must decrypt using the same password, MorrisseyMarr.

For .NET developers the System.Security.Cryptography namespace provides support for symmetric encryption. Each symmetric encryption provider is derived from the SymmetricAlgorithm base class:

System.Object 
  System.Security.Cryptography.SymmetricAlgorithm
    System.Security.Cryptography.Aes
    System.Security.Cryptography.DES
    System.Security.Cryptography.RC2
    System.Security.Cryptography.Rijndael
    System.Security.Cryptography.TripleDES

Each of the previous classes is found in mscorlib.dll. There is no need to create a reference within a project to access this assembly.  An example of TripleDes using a 16 byte key (128-bit key) to encrypt and decrypt symmetrically is as follows:

using System;
using System.IO;
using System.Linq;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Security.Cryptography;

namespace SymmetricEncryption
{
  class Program
  {
    static void Main(string[] args)
    {
      // 16 bytes, a.k.a. 128 bits
      const string topSecretSecurityKey = "0123456789012345"; 
      byte[] topSecretSecurityKeyReady =       
          System.Text.UTF8Encoding.UTF8.GetBytes(
              topSecretSecurityKey);
      const string data = 
          "If we are mark’d to die, we are enow. " + 
          "To do our country loss; and if to live, " +
          "The fewer men, the greater share of honour.";
      byte[] dataAsBytes = 
         System.Text.Encoding.UTF8.GetBytes(data);

      var provider = new TripleDESCryptoServiceProvider();

      provider.Key = topSecretSecurityKeyReady;
      provider.Mode = CipherMode.CBC;
      provider.Padding = PaddingMode.PKCS7;

      byte[] encryptedData = null;

      using (var encryptor = provider.CreateEncryptor())
      {
        encryptedData = encryptor.TransformFinalBlock(
                            dataAsBytes, 
                            0, 
                            dataAsBytes.Length);
      }

      Trace.Assert(Enumerable.SequenceEqual(
                        dataAsBytes, 
                        encryptedData) == false);

      byte[] decryptedData = null;

      using (var decryptor = provider.CreateDecryptor())
      {
        decryptedData = decryptor.TransformFinalBlock(
             encryptedData, 
             0, 
             encryptedData.Length);
      }

      Trace.Assert(Enumerable.SequenceEqual(
                       dataAsBytes, 
                       decryptedData) == true);
    }
  }
}

Asymmetric Encryption

The standard mechanism used for encryption is what is called asymmetric. In this model a public key is made available publicly. Any application needing to send encrypted data can use the public key to encrypt data. The rub is, the public key is incapable of decrypting the data. The decrypting application contains a private key that is paired with the public key. Only the public key can decrypt data encrypted with the public key. Unlike symmetric encryption where both parties have the master key, with asymmetric encryption there is only one copy of the master key -- the private key.

The System.Security.Cryptography namespace contains the algorithms that implement asymmetric encryption. All providers implementing this encryption are derived from the AsymmetricAlgorithm base class:

System.Object
  System.Security.Cryptography.AsymmetricAlgorithm
    System.Security.Cryptography.DSA
    System.Security.Cryptography.ECDiffieHellman
    System.Security.Cryptography.ECDsa
    System.Security.Cryptography.RSA

Each of the previous classes is found in mscorlib.dll or System.Core.dll. There is no need to create a reference within a project to access this assembly since these assemblies are normally referenced.  An example of  a class implemented using TripleDes and a 256 byte key (2048-bit key) to encrypt and decrypt symmetrically is presented below.

A helper class is needed into which to place the public/private keys which are generated from the same aysmetric provider instance:

using System;

namespace AsymmetricEncryption
{
  public class PublicPrivateKeys
  {
    public PublicPrivateKeys(string publicKey, string privateKey)
    {
      Public = publicKey;
      Private = privateKey;
    }

    public string Public { get; private set; }

    public string Private { get; private set; }
  }
}

The class gets the keys and exposes Encrypt/Decrypt methods is as follows:

using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Security.Cryptography;

namespace AsymmetricEncryption
{
  public class AsymmetricEncryption
  {
    private const int _keySizeBits = 2048;

    private static bool _optimalAsymmetricEncryptionPadding = false;

    public static PublicPrivateKeys GetKeys()
    {
      using (var provider = new RSACryptoServiceProvider(_keySizeBits))
      {
        // Generate public key and private key at the same time or the will not be valid
        var publicKey = provider.ToXmlString(false);
        var publicAndPrivateKey = provider.ToXmlString(true);

        return new PublicPrivateKeys(publicKey, publicAndPrivateKey);
      }
    }

    public static int GetMaxDataLength(int keySize)
    {
      if (_optimalAsymmetricEncryptionPadding)
      {
        return ((keySize - 384) / 8) + 7;
      }
      return ((keySize - 384) / 8) + 37;
    }

    public static bool IsKeySizeValid(int keySize)
    {
      return keySize >= 384 &&
              keySize <= 16384 &&
              keySize % 8 == 0;
    }
    public static string Encrypt(string text, string publicKeyXml)
    {
      var encrypted = Encrypt(Encoding.UTF8.GetBytes(text), publicKeyXml);

      return Convert.ToBase64String(encrypted);
    }

    public static byte[] Encrypt(byte[] data, string publicKeyXml)
    {
      int maxLength = GetMaxDataLength(_keySizeBits);
      if (data.Length > maxLength)
      {
        throw new ArgumentException(String.Format("Maximum data length is {0}", maxLength), "data");
      }

      if (!IsKeySizeValid(_keySizeBits))
      {
        throw new ArgumentException("Key size is not valid", "keySize");
      }

      if (String.IsNullOrEmpty(publicKeyXml))
      {
        throw new ArgumentException("Key is null or empty", "publicKeyXml");
      }

      using (var provider = new RSACryptoServiceProvider(_keySizeBits))
      {
        provider.FromXmlString(publicKeyXml);

        return provider.Encrypt(data, _optimalAsymmetricEncryptionPadding);
      }
    }

    public static string Decrypt(string text, 
                                 string publicAndPrivateKeyXml)
    {
      var decrypted = Decrypt(Convert.FromBase64String(text), 
                              publicAndPrivateKeyXml);

      return Encoding.UTF8.GetString(decrypted);
    }

    public static byte[] Decrypt(byte[] data, 
                                 string publicAndPrivateKeyXml)
    {
      if (!IsKeySizeValid(_keySizeBits))
      {
        throw new ArgumentException(
             "Key size is not valid", "keySize");
      }

      if (String.IsNullOrEmpty(publicAndPrivateKeyXml))
      {
        throw new ArgumentException(
            "Key is null or empty", "publicAndPrivateKeyXml");
      }

      using (var provider = 
                 new RSACryptoServiceProvider(_keySizeBits))
      {
        provider.FromXmlString(publicAndPrivateKeyXml);

        return provider.Decrypt(
               data, _optimalAsymmetricEncryptionPadding);
      }
    }
  }
}

The method that demonstrates keys being retrieved, data being encrypted and decrypted is as follows:

using System;

namespace AsymmetricEncryption
{
  public class PublicPrivateKeys
  {
    public PublicPrivateKeys(string publicKey, string privateKey)
    {
      Public = publicKey;
      Private = privateKey;
    }

    public string Public { get; private set; }

    public string Private { get; private set; }
  }
}

In the real world, the public key would be distributed to the entities that needed to encrypt data. The private key would be kept in a secure location and the encrypt/decrypt kept entirely separate from each other.



Friday, April 18, 2014

C#: Extension Methods (studying for Exam 70-483; Programming in C#)

Overview

C# extension methods were released as part of C# 3 in 2007 (Visual Studio 2008). In order to understand C# extension methods, consider .NET String class:

[SerializableAttribute]
[ComVisibleAttribute(true)]
public sealed class String : IComparable, 
ICloneable, IConvertible, IComparable<string>, 
        IEnumerable<char>, IEnumerable, IEquatable<string>

The sealed keyword means (to quote MSDN), "When applied to a class, the sealed modifier prevents other classes from inheriting from it." With respect to the String class there is no way to extend it through inheritance because it is sealed. This is where extension methods come in.

Using extension methods in an application can add methods to existing types such as the String class. These methods can be added without creating a new derived type (since this obviously not permitted due to the sealed keyword) or even recompiling the assembly for the original type. The String class or any type to which extension methods are applied remains unmodified.

When invoked by C# or VB.NET, extension methods  appear to be part of the type they are extending. For example it would be possible to extend the string class with methods ToGerman, ToFrench and ToItalian. The ToGerman method could be invoked as follows in the code:

string whatJFKDidNotSay = "I am a jelly doughnut.";
string aufDeutsch = whatJFKDidNotSay.ToGerman();

Even those with a rudimentary understanding of German recognize the translation to German is "Ich bin ein Pfannkuchen."

Extension methods are implemented using static method that is ultimately invoked as if the extension method were an instance method. An example of an extension method is as follows:

public static class StringGermanTranslation
{
  // The worst English to German translator every coded
  public static string ToGerman(this String text)
  {
    if (text == "I am a jelly doughnut.")
    {
      return "Ich bin ein Pfannkuchen.";
    }

    else
    {
      throw new Exception(
          "Unknown phrase cannot be translated: " + text);
    }
  }
}

The signature of the extension method is significant. The method is decorated with the static keyword and the first parameter is prefixed by the this keyword. Following "this" the type to be extended is specified (a.k.a. String). It is possible pass parameters to extension methods provided the type extended is the first parameter. To demonstrate this consider:

public static string ToGerman(this String text, 
                              bool fromNorthernGermany)
{
  if (text == "Saturday")
  {
    if (fromNorthernGermany)
    {
      return "Sonnabend.";
    }

    else
    {
      return "Samstag.";
    }
  }

  else
  {
    throw new Exception(
        "Unknown phrase cannot be translated: " + text);
  }
}

The second parameter of ToGerman allows a region to be specified so return a translation in either Northern German or Southern German. In reality the name used for Saturday in German is more complex than Northern versus Southern Germany. 

The overloaded method of ToGerman including a bool to specify region is invoked as follows:

string day = "Saturday";
// true since we are from northern Germany
string tagAufDeutsch = day.ToGerman(true);

LINQ 

Many developers have noticed that their code generate a significant number of compilation errors if the remove the following from a C# source file:
using System.Linq;

LINQ added query functionality to the types:
  • System.Collections.IEnumerable
  • System.Collections.Generic.IEnumerable<T>

The mechanism to extend the aforementioned types is extension methods. When "using System.Linq;" is omitted, the LINQ provided extension methods are not recognized and generate a variety of compiler errors.

Extending Interfaces

If LINQ can extend a .NET interface, any C# or VB.NET developer can extend an interface. Consider the following class which extends .NET IDisposable interface:

public static class ExtendIDisposable
{
  public static void DisposeAndDisplayMessage(
             this IDisposable idisposable, string message)
  {
    Console.WriteLine(message);
    idisposable.Dispose();
  }
}

The following code can be written making use of the DisposeAndDisplayMessage extension method: 

using (var file = File.CreateText(Path.GetTempFileName()))
{
  file.WriteLine("Hi mom, I miss you.");
  file.DisposeAndDisplayMessage(
    "Guess it's time to close the file. Bye mom.");
}

The method File.CreateText returns type System.IO.StreamWriter. StreamWriter implements IDisposable so our extension method changed the behavior of a StreamWriter class via IDisposable.

Null Instances

Consider the following code where the value of the file variable was assigned to null below:
using (var file = File.CreateText(Path.GetTempFileName()))
{
  file.WriteLine("Hi mom, I miss you.");
  file = null;
  file.DisposeAndDisplayMessage(
    "Guess it's time to close the file. Bye mom.");
}

The question is, "Will DisposeAndDisplayMessage be invoked?" The answer is yes. The DisposeAndDisplayMessage  is invoked. Recall this method is implemented as follows:

  public static void DisposeAndDisplayMessage(
             this IDisposable idisposable, string message)
  {
    Console.WriteLine(message);
    idisposable.Dispose();
  }

The line of code "idisposable.Dispose();" will generate a null access exception. When implementing extension methods develops need to recognize the first parameter corresponding to the type extended can be null.

Tuesday, April 15, 2014

Visual Studio 2013: Displaying C# Partial Classes as a Hierarchy in Solution Explorer

Goal


This post demonstrates how to make C# partial classes display as a hierarchy within a Visual Studio 2013 project. To demonstrate consider the partial class DataTier which is composed of three source files: DataTier.cs, DataTier.Query.cs and DataTier.CreateUpdateDelete.cs. The aforementioned files appear in Visual Studio's Solution Explorer (View | Solution Explorer) as follows:


The files appear in non-hierarchical fashion because they were each created by right clicking on the project and selecting Add | Class from the context menu. Visual Studio by default simply alphabetizes the file when it displays them in Solution Explorer. This post demonstrates how to display the files as follows, in a hierarchical fashion:


Notice in the previous screenshot that DataTier.cs is the root file displayed and  DataTier.Query.cs and DataTier.CreateUpdateDelete.cs are displayed underneath.

Background

A C# class prefixed by the partial keyword allows the class definition to span multiple source files. Using multiple source files per-class is desirable to:
  1. allow multiple developers to work on a class but allows each developer to be siloed which simplifies code sharing.  
  2. generated code, where the class to be defined such that there is a source file for manually created code (code created by a developer) and a source file for automatically generated such as the code created by the designers for Windows Forms, WPF and ASP.NET.
A case where partial classes can be used is in developing a data base API where the following files are defined:
  • DataTier.cs: contains constants shared by methods that query and methods that create, update and delete.
  • DataTier.Query.cs: contains the methods associated with querying data.
  • DataTier.CreateUpdateDelete.cs: contains the methods associated with creating new objects, updating existing objects and deleting existing objects.

Solution

As was mentioned previously, creating three source files by right clicking on the project in Solution Explorer and selecting Add | Class from the context menu, results in the files being displayed in non-hierarchical fashion. When Visual Studio creates Windows Forms, ASP.NET Forms and WPF Forms, it displays said files in a hierarchical fashion. To display the three partial source files associated with the DataTier class as a hierarchy, is a matter of reverse engineering a project that contains a hierarchical display of files. Visual Studio's ASP.NET Web Forms Application project template creates a project with parent/child files displayed within Solution Explorer. 

An ASP.NET Web Forms Application project is created in Visual Studio 2013 by selecting File | New | Project | Templates | Visual C# | Web | ASP.NET Web Application, specifically the Web Forms template:


To the right of the previous screenshot notice that for the class, About, there are two files under About.aspx: About,aspx.designer.cs and About.aspx.cs. The previous screenshot shows to the left that these files are just the source files associated with the About partial class.

Within the project file, WebApplication.cspoj, the files associated with the About web form are represented as follows:


Each child source file contains DependentUpon element that specified About.aspx.cs and About.aspx.designer.cs should be displayed under About.cs.

The source files associated with the DataTier partial class are found in the project file, ConsoleApplication1.csproj. The DataTier partial class files within  ConsoleApplication1.csproj are represented as follows:


By creating a DependentUponchild element for DataTier.Query.cs and DataTier.CreateUpdateDelete.cs, these files can be displayed under DataTier.cs within Solution Explorer:


The previous changes have to be made by hand using Notepad, Notepad++ or any text editor. The result when the following changes are made and the project is opened by Visual Studio is the files are displayed as a hierarchy:


This approach works with Visual Studio 2005, Visual Studio 2008, Visual Studio 2012 and Visual Studio 2013. In the future Microsoft may get around to adding the feature directly to Visual Studio. Users creating partial classes is a fairly rare event so moving forward partial classes may just need to be manipulated by hand to get the desired display within Solution Explorer.