SOLID principles in .NET revisited part 3: the Open-Closed principle

Introduction

In the previous post we looked at the letter ‘S’ in SOLID, i.e. the single responsibility principle. We identified the reasons for change in the OnDemandAgentService class and broke out 3 of them into separate classes. The OnDemandAgentService can now concentrate on starting a new on-demand agent and delegate other tasks to specialised services such as EmailService. Also, an additional benefit is that other classes in the assembly can re-use those specialised services so that e.g. logging can be centralised.

In this post we’ll look at the second constituent of the SOLID principles, the letter ‘O’ which stands for the Open-Closed Principle OCP.

Read more of this post

Advertisement

SOLID principles in .NET revisited part 2: Single Responsibility Principle

Introduction

In the previous post we set the tone for the recycled series on SOLID. We also presented a piece of code to be improved. There are several issues with this code and we’ll try to improve it step by step as we go through the principles behind SOLID.

In this post we’ll start with ‘S’, i.e. the Single Responsibility Principle (SRP). We’ll partly improve the code by applying SRP to it. Note that the code will still have a lot of faults left after this but we’ll improve it gradually.

Read more of this post

SOLID principles in .NET revisited part 1: introduction with code to be improved

Introduction

Probably every single programmer out there wants to write good code. Nobody has the desire to be ashamed of the code base they have written. Probably no programmer wants to turn a large software project into a failure by deliberately writing low quality code.

What is good code anyway? Opinions differ on this point but we can generally say that good code means code that is straightforward to extend and maintain, code that’s easy to test, code that is flexible, code that is relatively easy to read, code that is difficult to break and code that can swiftly be adapted to changes in the requirements without weeks of refactoring. These traits are interdependent. E.g. code that’s flexible will be easier to change in line with new requirements. The English word “solid” has the meaning of “difficult to break” or “resistant to change” which is naturally applicable to good code.

However, it’s very difficult to write good code in practice. On the other hand it’s very easy to write bad code. Compilers do not understand software engineering principles so they won’t complain if your code is “bad” in any way – except if your code contains faulty syntax but that’s not what we mean by bad code here. Modern object oriented languages like C#, Java or Python provide a lot of flexibility to the programmer. He or she can construct code which performs one or more functions in lots of different ways. Also, different programmers might point out different parts in the same code base as being “bad”.

Read more of this post

Externalising dependencies with Dependency Injection in .NET part 10: hashing and digital signatures

Introduction

In the previous post of this series we looked at how to hide the implementation of asymmetric encryption operations. In this post we’ll look at abstracting away hashing and digital signatures as they often go hand in hand. Refer back to this post on hashing and this on digital signatures if you are not familiar with those concepts.

We’ll continue directly from the previous post and extend the Infrastructure layer of our demo app we’ve been working on so far. So have it ready in Visual Studio and let’s get to it.

Preparation

Add two new subfolders called Hashing and DigitalSignature to the Cryptography folder of the Infrastructure.Common library. We’ll follow the same methodology as in the case of asymmetric encryption, i.e. we’ll communicate with proper objects as parameters and return types instead of writing a large amount of overloaded methods.

Recall that we had a common abstract class to all return types in the IAsymmetricCryptographyService interface: ResponseBase. We’ll reuse it here.

Hashing

In short hashing is a one way encryption. A plain text is hashed and there’s no key to decipher the hashed value. A common application of hashing is storing passwords. Any hashing algorithm should be able to hash plain string and that’s the only expectation we have. Add the below interface to the Hashing folder:

public interface IHashingService
{
	HashResult Hash(string message);
	string HashAlgorithmCode();
}

…where HashResult will hold the readable and byte array formats of the hashed value and has the following form:

public class HashResult : ResponseBase
{
	public string HexValueOfHash { get; set; }
	public byte[] HashedBytes { get; set; }
}

What is HashAlgorithmCode() doing there? The implementation if the interface will return a short description of itself, such as “SHA1” or “SHA512”. We’ll see later that it’s required by the digital signature implementation.

This can actually be seen as a violation of ‘L‘ in SOLID as we’re specifying the type of the implementation in some form. However, I think it serves a good purpose here and it will not be misused in some funny switch statement where we check the string returned by HashAlgorithmCode() in order to direct the logic. Let’s insert two implementations of IHashingService to the Hashing folder. Here comes SHA1:

public class Sha1ManagedHashingService : IHashingService
{
	public HashResult Hash(string message)
	{
		HashResult hashResult = new HashResult();
		try
		{
			SHA1Managed sha1 = new SHA1Managed();
			byte[] hashed = sha1.ComputeHash(Encoding.UTF8.GetBytes(message));
			string hex = Convert.ToBase64String(hashed);
			hashResult.HashedBytes = hashed;
			hashResult.HexValueOfHash = hex;
		}
		catch (Exception ex)
		{
			hashResult.ExceptionMessage = ex.Message;
		}
		return hashResult;
	}

	public string HashAlgorithmCode()
	{
		return "SHA1";
	}
}

…and here’s SHA512:

public class Sha512ManagedHashingService : IHashingService
{
	public HashResult Hash(string message)
	{
		HashResult hashResult = new HashResult();
		try
		{
			SHA512Managed sha512 = new SHA512Managed();
			byte[] hashed = sha512.ComputeHash(Encoding.UTF8.GetBytes(message));
			string hex = Convert.ToBase64String(hashed);
			hashResult.HashedBytes = hashed;
			hashResult.HexValueOfHash = hex;
		}
		catch (Exception ex)
		{
			hashResult.ExceptionMessage = ex.Message;
		}
		return hashResult;
	}

	public string HashAlgorithmCode()
	{
		return "SHA512";
	}
}

That’s enough for starters.

Digital signatures

Digital signatures in a nutshell are used to electronically sign the hash of a message. The receiver of the message can then check if the message has been tampered with in any way. Also, a digital signature can prove that it was the sender who sent a particular message. E.g. a receiver may only accept signed messages so that the sender cannot claim that it was someone else who sent the message.

It is not “compulsory” to encrypt a signed message. You can sign the hash of an unencrypted message. However, for extra safety the hash is also often encrypted. The process is the following:

  • The Sender encrypts a message with the Receiver’s public key
  • The Sender then hashes the cipher text
  • The Sender signs the hashed cipher with their private key
  • The Sender transmits the cipher text, the hash and the signature to the Receiver
  • The Receiver verifies the signature with the Sender’s public key
  • If the signature is correct then the Receiver deciphers the cipher text with their full asymmetric key

The following interface will reflect what we need from a digital signature service: sign a message and then verify the signature:

public interface IEncryptedDigitalSignatureService
{
	DigitalSignatureCreationResult Sign(DigitalSignatureCreationArguments arguments);
	DigitalSignatureVerificationResult VerifySignature(DigitalSignatureVerificationArguments arguments);
}

DigitalSignatureCreationResult will hold the signature and the cipher text:

public class DigitalSignatureCreationResult : ResponseBase
{
	public byte[] Signature { get; set; }
	public string CipherText { get; set; }
}

DigitalSignatureCreationArguments will carry the message to be encrypted, hashed and signed. It will also hold the the full key set for the signature and the public key of the receiver for encryption:

public class DigitalSignatureCreationArguments
{
	public string Message { get; set; }
	public XDocument FullKeyForSignature { get; set; }
	public XDocument PublicKeyForEncryption { get; set; }
}

Then we have DigitalSignatureVerificationResult which has properties to check if the signatures match and the decoded text:

public class DigitalSignatureVerificationResult : ResponseBase
{
	public bool SignaturesMatch { get; set; }
	public string DecodedText { get; set; }
}

…and finally we have DigitalSignatureVerificationArguments which holds the properties for the Sender’s public key for signature verification, the Receiver’s full key for decryption, the cipher text and the signature:

public class DigitalSignatureVerificationArguments
{
	public XDocument PublicKeyForSignatureVerification { get; set; }
	public XDocument FullKeyForDecryption { get; set; }
	public string CipherText { get; set; }
	public byte[] Signature { get; set; }
}

RsaPkcs1 digital signature provider

We’ll use the built-in RSA-based signature provider in .NET for the implementation. It will need to hash the cipher text so it’s good that we’ve prepared IHashingService in advance. Here comes the implementation where you’ll see how the HashAlgorithmCode() method is used in specifying the hash algorithm of the signature formatter and deformatter:

public class RsaPkcs1DigitalSignatureService : IEncryptedDigitalSignatureService
{
	private readonly IHashingService _hashingService;

	public RsaPkcs1DigitalSignatureService(IHashingService hashingService)
	{
		if (hashingService == null) throw new ArgumentNullException("Hashing service");
		_hashingService = hashingService;
	}

	public DigitalSignatureCreationResult Sign(DigitalSignatureCreationArguments arguments)
	{
		DigitalSignatureCreationResult res = new DigitalSignatureCreationResult();
		try
		{				
			RSACryptoServiceProvider rsaProviderReceiver = new RSACryptoServiceProvider();
			rsaProviderReceiver.FromXmlString(arguments.PublicKeyForEncryption.ToString());
			byte[] encryptionResult = rsaProviderReceiver.Encrypt(Encoding.UTF8.GetBytes(arguments.Message), false);
			HashResult hashed = _hashingService.Hash(Convert.ToBase64String(encryptionResult));

			RSACryptoServiceProvider rsaProviderSender = new RSACryptoServiceProvider();
			rsaProviderSender.FromXmlString(arguments.FullKeyForSignature.ToString());
			RSAPKCS1SignatureFormatter signatureFormatter = new RSAPKCS1SignatureFormatter(rsaProviderSender);
			signatureFormatter.SetHashAlgorithm(_hashingService.HashAlgorithmCode());
			byte[] signature = signatureFormatter.CreateSignature(hashed.HashedBytes);

			res.Signature = signature;
			res.CipherText = Convert.ToBase64String(encryptionResult);
			res.Success = true;
		}
		catch (Exception ex)
		{
			res.ExceptionMessage = ex.Message;
		}

		return res;
	}

	public DigitalSignatureVerificationResult VerifySignature(DigitalSignatureVerificationArguments arguments)
	{
		DigitalSignatureVerificationResult res = new DigitalSignatureVerificationResult();
		try
		{
			RSACryptoServiceProvider rsaProviderSender = new RSACryptoServiceProvider();
			rsaProviderSender.FromXmlString(arguments.PublicKeyForSignatureVerification.ToString());
			RSAPKCS1SignatureDeformatter deformatter = new RSAPKCS1SignatureDeformatter(rsaProviderSender);
			deformatter.SetHashAlgorithm(_hashingService.HashAlgorithmCode());				
			HashResult hashResult = _hashingService.Hash(arguments.CipherText);
			res.SignaturesMatch = deformatter.VerifySignature(hashResult.HashedBytes, arguments.Signature);
			if (res.SignaturesMatch)
	        	{
        			RSACryptoServiceProvider rsaProviderReceiver = new RSACryptoServiceProvider();
  	        		rsaProviderReceiver.FromXmlString(arguments.FullKeyForDecryption.ToString());
		        	byte[] decryptedBytes = rsaProviderReceiver.Decrypt(Convert.FromBase64String(arguments.CipherText), false);
				res.DecodedText = Encoding.UTF8.GetString(decryptedBytes);
			}

			res.Success = true;
		}
		catch (Exception ex)
		{
			res.ExceptionMessage = ex.Message;
		}

		return res;
	}
}

The whole chain can be tested as follows:

private static void TestDigitalSignatureService()
{
	IAsymmetricCryptographyService asymmetricService = new RsaAsymmetricCryptographyService();
	AsymmetricKeyPairGenerationResult keyPairGenerationResultReceiver = asymmetricService.GenerateAsymmetricKeys(1024);
	AsymmetricKeyPairGenerationResult keyPairGenerationResultSender = asymmetricService.GenerateAsymmetricKeys(1024);

	IEncryptedDigitalSignatureService digitalSignatureService = new RsaPkcs1DigitalSignatureService(new Sha1ManagedHashingService());

	DigitalSignatureCreationArguments signatureCreationArgumentsFromSender = new DigitalSignatureCreationArguments()
	{
		Message = "Text to be encrypted and signed"
		, FullKeyForSignature = keyPairGenerationResultSender.FullKeyPairXml
		, PublicKeyForEncryption = keyPairGenerationResultReceiver.PublicKeyOnlyXml
	};
	DigitalSignatureCreationResult signatureCreationResult = digitalSignatureService.Sign(signatureCreationArgumentsFromSender);
	if (signatureCreationResult.Success)
	{
		DigitalSignatureVerificationArguments verificationArgumentsFromReceiver = new DigitalSignatureVerificationArguments();
		verificationArgumentsFromReceiver.CipherText = signatureCreationResult.CipherText;
		verificationArgumentsFromReceiver.Signature = signatureCreationResult.Signature;
		verificationArgumentsFromReceiver.PublicKeyForSignatureVerification = keyPairGenerationResultSender.PublicKeyOnlyXml;
		verificationArgumentsFromReceiver.FullKeyForDecryption = keyPairGenerationResultReceiver.FullKeyPairXml;
		DigitalSignatureVerificationResult verificationResult = digitalSignatureService.VerifySignature(verificationArgumentsFromReceiver);
		if (verificationResult.Success)
		{
			if (verificationResult.SignaturesMatch)
			{
				Console.WriteLine("Signatures match, decoded text: {0}", verificationResult.DecodedText);
			}
			else
			{
				Console.WriteLine("Signature mismatch!!");
			}
		}
		else
		{
			Console.WriteLine("Signature verification failed: {0}", verificationResult.ExceptionMessage);
		}
	}
	else
	{
		Console.WriteLine("Signature creation failed: {0}", signatureCreationResult.ExceptionMessage);
	}
}

Check how the keys of each party in the messaging conversation are used for encrypting, signing, verifying and decrypting the message.

So that’s it for digital signatures.

This was the last post in the series on object dependencies. We could take up a number of other cross cutting concerns for the infrastructure layer, such as:

  • Http calls
  • User-level security, such as a role provider, a claims provider etc.
  • Session provider
  • Random password generation

…and possibly a lot more. By now you have a better understanding how to externalise tightly coupled object dependencies in your classes to make them more flexible and testable.

View the list of posts on Architecture and Patterns here.

Externalising dependencies with Dependency Injection in .NET part 9: cryptography part 2

Introduction

In the previous post of this series we looked at how to hide the implementation of symmetric encryption operations. In this post we’ll look at abstracting away asymmetric cryptography. Refer back to this post in case you are not familiar with asymmetric encryption in .NET.

We’ll continue directly from the previous post and extend the Infrastructure layer of our demo app we’ve been working on so far. So have it ready in Visual Studio and let’s get to it.

Preparation

Add a new subfolder called Asymmetric to the Cryptography folder of the Infrastructure.Common library. We’ll follow the same methodology as in the case of emailing and symmetric encryption, i.e. we’ll communicate with proper objects as parameters and return types instead of writing a large amount of overloaded methods.

Recall that we had a common abstract class to all return types in the ISymmetricCryptographyService interface: ResponseBase. We’ll reuse it here. There’s one more class we’ll use again here and that is CipherTextDecryptionResult.

Asymmetric encryption

In summary asymmetric encryption means that you have a pair of keys: a public and a private key. This is in contrast to symmetric encryption where there is only one key. The public key can be distributed to those who wish to send you an encrypted message. The private key will be used to decrypt the incoming cipher text. The public and private keys are in fact also symmetric: you can encrypt a message with the public key and decrypt with the private one and vice versa. As asymmetric encryption is considerably slower – but much more secure – than symmetric encryption it’s usually not used to encrypt large blocks of plain text. Instead, the sender encrypts the text with a symmetric key and then encrypts the symmetric key with the receiver’s public asymmetric key. The receiver will then decrypt the symmetric key using their asymmetric private key and decrypt the message with the decrypted symmetric key. If you want to see that in action I have a sample project which takes up this process starting here.

Our expectations from any reasonable asymmetric encryption mechanism are the same as for symmetric algorithms:

  • Create a valid key – actually a pair of keys – of a given size in bits
  • Encrypt a text
  • Decrypt a cipher text

Add the following interface to the Asymmetric folder to reflect these expectations:

public interface IAsymmetricCryptographyService
{
	AsymmetricKeyPairGenerationResult GenerateAsymmetricKeys(int keySizeInBits);
	AsymmetricEncryptionResult Encrypt(AsymmetricEncryptionArguments arguments);
	CipherTextDecryptionResult Decrypt(AsymmetricDecryptionArguments arguments);
}

We already know CipherTextDecryptionResult, here’s a reminder:

public class CipherTextDecryptionResult : ResponseBase
{
	public string DecodedText { get; set; }
}

AsymmetricKeyPairGenerationResult will represent the keys in XML format. We’ll need to represent the keys in two forms. The full representation is where the private key is included and should be kept with you. Another representation is where only the public key part of the pair is shown which can be used to distribute to your partners. Insert a class called AsymmetricKeyPairGenerationResult to the Asymmetric folder:

public class AsymmetricKeyPairGenerationResult : ResponseBase
{
	private XDocument _fullKeyPairXml;
	private XDocument _publicKeyOnlyXml;

	public AsymmetricKeyPairGenerationResult(XDocument fullKeyPairXml, XDocument publicKeyOnlyXml)
	{
		if (fullKeyPairXml == null) throw new ArgumentNullException("Full key pair XML");
		if (publicKeyOnlyXml == null) throw new ArgumentNullException("Public key only XML");
		_fullKeyPairXml = fullKeyPairXml;
		_publicKeyOnlyXml = publicKeyOnlyXml;
	}

	public XDocument FullKeyPairXml
	{
		get
		{
			return _fullKeyPairXml;
		}
	}

	public XDocument PublicKeyOnlyXml
	{
		get
		{
			return _publicKeyOnlyXml;
		}
	}
}

The keys are of type XDocument to make sure that they are in fact valid XML.

AsymmetricEncryptionResult will have two properties: the cipher text as a base64 string and as a byte array:

public class AsymmetricEncryptionResult : ResponseBase
{
	public string CipherText { get; set; }
	public byte[] CipherBytes { get; set; }
}

To encrypt a message we’ll need a plain text and the public-key-only part of the asymmetric keys:

public class AsymmetricEncryptionArguments
{
	public XDocument PublicKeyForEncryption { get; set; }
	public string PlainText { get; set; }		
}

Finally in order to decrypt a cipher text we’ll need the complete set of keys and the cipher text itself:

public class AsymmetricDecryptionArguments
{
	public XDocument FullAsymmetricKey { get; set; }
	public string CipherText { get; set; }
}

The project should compile at this stage.

The implementation: RSA

We’ll go for one of the most widely used implementation of asymmetric encryption, RSA. .NET has good support for it so the implementation if straightforward. Insert a class called RsaAsymmetricCryptographyService into the Asymmetric folder:

public class RsaAsymmetricCryptographyService : IAsymmetricCryptographyService
{
	public AsymmetricKeyPairGenerationResult GenerateAsymmetricKeys(int keySizeInBits)
	{
		try
		{
			RSACryptoServiceProvider myRSA = new RSACryptoServiceProvider(keySizeInBits);
			XDocument publicKeyXml = XDocument.Parse(myRSA.ToXmlString(false));
			XDocument fullKeyXml = XDocument.Parse(myRSA.ToXmlString(true));
			return new AsymmetricKeyPairGenerationResult(fullKeyXml, publicKeyXml) { Success = true };
		}
		catch (Exception ex)
		{
			return new AsymmetricKeyPairGenerationResult(new XDocument(), new XDocument()) { ExceptionMessage = ex.Message };
		}
	}

	public AsymmetricEncryptionResult Encrypt(AsymmetricEncryptionArguments arguments)
	{
		AsymmetricEncryptionResult encryptionResult = new AsymmetricEncryptionResult();
		try
		{
			RSACryptoServiceProvider myRSA = new RSACryptoServiceProvider();
			string rsaKeyForEncryption = arguments.PublicKeyForEncryption.ToString();
			myRSA.FromXmlString(rsaKeyForEncryption);
			byte[] data = Encoding.UTF8.GetBytes(arguments.PlainText);
			byte[] cipherText = myRSA.Encrypt(data, false);
			encryptionResult.CipherBytes = cipherText;
			encryptionResult.CipherText = Convert.ToBase64String(cipherText);
			encryptionResult.Success = true;
		}
		catch (Exception ex)
		{
			encryptionResult.ExceptionMessage = ex.Message;
		}
		return encryptionResult;
	}

	public CipherTextDecryptionResult Decrypt(AsymmetricDecryptionArguments arguments)
	{
		CipherTextDecryptionResult decryptionResult = new CipherTextDecryptionResult();
		try
		{
			RSACryptoServiceProvider cipher = new RSACryptoServiceProvider();
			cipher.FromXmlString(arguments.FullAsymmetricKey.ToString());
			byte[] original = cipher.Decrypt(Convert.FromBase64String(arguments.CipherText), false);
			decryptionResult.DecodedText = Encoding.UTF8.GetString(original);
			decryptionResult.Success = true;
		}
		catch (Exception ex)
		{
			decryptionResult.ExceptionMessage = ex.Message;
		}
		return decryptionResult;
	}
}

If you’re already familiar with asymmetric cryptography in .NET this should be straightforward. Otherwise consult the above link for the technical details, I won’t repeat them here.

The methods in the interface can be chained together in an example as follows:

private static void TestAsymmetricEncryptionService()
{
	IAsymmetricCryptographyService asymmetricSevice = new RsaAsymmetricCryptographyService();
	AsymmetricKeyPairGenerationResult keyPairGenerationResult = asymmetricSevice.GenerateAsymmetricKeys(1024);
	if (keyPairGenerationResult.Success)
	{
		AsymmetricEncryptionArguments encryptionArgs = new AsymmetricEncryptionArguments()
		{
			PlainText = "Text to be encrypted"
			, PublicKeyForEncryption = keyPairGenerationResult.PublicKeyOnlyXml
		};
		AsymmetricEncryptionResult encryptionResult = asymmetricSevice.Encrypt(encryptionArgs);
		if (encryptionResult.Success)
		{
			Console.WriteLine("Ciphertext: {0}", encryptionResult.CipherText);
			AsymmetricDecryptionArguments decryptionArguments = new AsymmetricDecryptionArguments()
			{
				CipherText = encryptionResult.CipherText
				, FullAsymmetricKey = keyPairGenerationResult.FullKeyPairXml
			};
			CipherTextDecryptionResult decryptionResult = asymmetricSevice.Decrypt(decryptionArguments);
			if (decryptionResult.Success)
			{
				Console.WriteLine("Decrypted text: {0}", decryptionResult.DecodedText);
			}
			else
			{
				Console.WriteLine("Decryption failure: {0}", decryptionResult.ExceptionMessage);
			}
		}
		else
		{
			Console.WriteLine("Encryption failure: {0}", encryptionResult.ExceptionMessage);
		}
	}
	else
	{
		Console.WriteLine("Asymmetric key generation failure: {0}", keyPairGenerationResult.ExceptionMessage);
	}
}

If you run this code you’ll see that the keys are generated, the plain text is encrypted and then decrypted again.

In the next post, which will be last post of this series on dependencies, we’ll look at two other aspects in cryptography: hashing and digital signatures.

View the list of posts on Architecture and Patterns here.

Externalising dependencies with Dependency Injection in .NET part 8: symmetric cryptography

Introduction

In the previous post of this series we looked at how to hide the implementation of emailing operations. In this post we’ll look at abstracting away cryptography. Cryptography is often needed to communicate between two systems in a safe way by e.g. exchanging public keys first.

If you don’t know how to use cryptography in a .NET project check out the posts on this page.

The first couple of posts of this series went through the process of making hard dependencies to loosely coupled ones. I won’t go through that again – you can refer back to the previous parts of this series to get an idea. You’ll find a link below to view all posts of the series. Another post on the Single Responsibility Principle includes an example of breaking out different dependencies that you can look at.

We’ll extend the Infrastructure layer of our demo app we’ve been working on so far. So have it ready in Visual Studio and let’s get to it.

Cryptography in .NET

We discussed Cryptography in .NET on this blog before:

If you’re interested in the technical details then please consult those pages. I won’t go into cryptography in .NET but rather concentrate on the dependency injection side of it. We’ll abstract away all four of the above topics. As it’s a lot of code I’ve decided to break up the post into three parts:

  • This post: symmetric encryption
  • Next post: asymmetric encryption
  • Last post: hashing and digital signatures

Preparation

Add a new folder called Cryptography to the Infrastructure.Common library. We’ll follow the same methodology as in the case of emailing and communicate with proper objects as parameters and return types instead of writing a large amount of overloaded methods.

All return types will have 2 properties in common: Success and ExceptionMessage. A lot of things can go wrong in cryptography: wrong key size, unsupported block size, incomplete public/private key etc. The boolean property Success will indicate if the process was carried out successfully. ExceptionMessage will store the exception message in case Success is false. Insert the following abstract class to the Cryptography folder:

public abstract class ResponseBase
{
	public bool Success { get; set; }
	public string ExceptionMessage { get; set; }
}

Symmetric encryption

Symmetric algorithm in a nutshell means that both the message sender and receiver have access to the same cryptographic key for both encryption and decryption.

Insert a subfolder called Symmetric within the Cryptography folder.

I think any reasonable symmetric encryption implementation should be able to perform the following 3 operations:

  • Create a valid key of a given size in bits
  • Encrypt a text
  • Decrypt a cipher text

Add the following interface to the Symmetric folder to reflect these expectations:

public interface ISymmetricCryptographyService
{
	SymmetricKeyGenerationResult GenerateSymmetricKey(int bitSize);
	SymmetricEncryptionResult Encrypt(SymmetricEncryptionArguments arguments);
	CipherTextDecryptionResult Decrypt(SymmetricDecryptionArguments arguments);
}

SymmetricKeyGenerationResult is simple: it will contain the symmetric key and inherit from ResponseBase:

public class SymmetricKeyGenerationResult : ResponseBase
{
	public string SymmetricKey { get; set; }
}

SymmetricEncryptionResult will contain the encoded version of the plain text, called cipher text, and an initialisation vector (IV) for increased security. Note that IV is not always used but it’s highly recommended as it adds randomness to the resulting cipher text making brute force decryption harder. Insert the following class to the Symmetric folder:

public class SymmetricEncryptionResult : ResponseBase
{
	public string CipherText { get; set; }
	public string InitialisationVector { get; set; }		
}

There’s a number of arguments that will be necessary to both encryption and decryption such as the key size and the common key. Add the following abstract class to the subfolder:

public class CommonSymmetricCryptoArguments
{
	public int KeySize { get; set; }
	public int BlockSize { get; set; }
	public PaddingMode PaddingMode { get; set; }
	public CipherMode CipherMode { get; set; }
	public string SymmetricPublicKey { get; set; }
}

Both SymmetricEncryptionArguments and SymmetricDecryptionArguments will inherit from the above base class. Insert the following classes to the Asymmetric subfolder:

public class SymmetricEncryptionArguments : CommonSymmetricCryptoArguments
{
	public string PlainText { get; set; }		
}
public class SymmetricDecryptionArguments : CommonSymmetricCryptoArguments
{
	public string CipherTextBase64Encoded { get; set; }
	public string InitialisationVectorBase64Encoded { get; set; }
}

So we’ll send in the plain text to be encrypted in SymmetricEncryptionArguments along with all other common arguments in CommonSymmetricCryptoArguments. Decryption will need the cipher text, the IV and the common arguments in CommonSymmetricCryptoArguments.

The last missing piece is CipherTextDecryptionResult. This object will be re-used for the asymmetric encryption section and will only include one extra property, namely the decoded text. Therefore add it to the Cryptography and not the Symmetric folder:

public class CipherTextDecryptionResult : ResponseBase
{
	public string DecodedText { get; set; }
}

The code should compile at this stage so we can move on with the implementation of the ISymmetricCryptographyService interface.

The implementation: Rijndael

For the implementation we’ll take the widely used Rijndael managed implementation of symmetric encryption available in .NET. Add a new class called RijndaelManagedSymmetricEncryptionService to the Symmetric folder:

public class RijndaelManagedSymmetricEncryptionService : ISymmetricCryptographyService
{
	public SymmetricEncryptionResult Encrypt(SymmetricEncryptionArguments arguments)
	{			
		SymmetricEncryptionResult res = new SymmetricEncryptionResult();
		try
		{
			RijndaelManaged rijndael = CreateCipher(arguments);
			res.InitialisationVector = Convert.ToBase64String(rijndael.IV);
			ICryptoTransform cryptoTransform = rijndael.CreateEncryptor();
			byte[] plain = Encoding.UTF8.GetBytes(arguments.PlainText);
			byte[] cipherText = cryptoTransform.TransformFinalBlock(plain, 0, plain.Length);
			res.CipherText = Convert.ToBase64String(cipherText);				
			res.Success = true;
		}
		catch (Exception ex)
		{
			res.ExceptionMessage = ex.Message;
		}
		return res;
	}

	public CipherTextDecryptionResult Decrypt(SymmetricDecryptionArguments arguments)
	{
		CipherTextDecryptionResult res = new CipherTextDecryptionResult();
		try
		{
			RijndaelManaged cipher = CreateCipher(arguments);
			cipher.IV = Convert.FromBase64String(arguments.InitialisationVectorBase64Encoded);
			ICryptoTransform cryptTransform = cipher.CreateDecryptor();
			byte[] cipherTextBytes = Convert.FromBase64String(arguments.CipherTextBase64Encoded);
			byte[] plainText = cryptTransform.TransformFinalBlock(cipherTextBytes, 0, cipherTextBytes.Length);
			res.DecodedText = Encoding.UTF8.GetString(plainText);
			res.Success = true;
		}
		catch (Exception ex)
		{
			res.ExceptionMessage = ex.Message;
		}
		return res;
	}

	public SymmetricKeyGenerationResult GenerateSymmetricKey(int bitSize)
	{
		SymmetricKeyGenerationResult res = new SymmetricKeyGenerationResult();
		try
		{
			byte[] key = new byte[bitSize / 8];
			RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
			rng.GetBytes(key);
			res.SymmetricKey = BitConverter.ToString(key).Replace("-", string.Empty);
			res.Success = true;
		}
		catch (Exception ex)
		{
			res.ExceptionMessage = ex.Message;
		}
		return res;
	}

	private RijndaelManaged CreateCipher(CommonSymmetricCryptoArguments arguments)
	{
		RijndaelManaged cipher = new RijndaelManaged();
		cipher.KeySize = arguments.KeySize;
		cipher.BlockSize =  arguments.BlockSize;
		cipher.Padding = arguments.PaddingMode;
		cipher.Mode = arguments.CipherMode;
		byte[] key = HexToByteArray(arguments.SymmetricPublicKey);
		cipher.Key = key;
		return cipher;
	}

	private byte[] HexToByteArray(string hexString)
	{
		if (0 != (hexString.Length % 2))
		{
			throw new ApplicationException("Hex string must be multiple of 2 in length");
		}
		int byteCount = hexString.Length / 2;
		byte[] byteValues = new byte[byteCount];
		for (int i = 0; i < byteCount; i++)
		{
			byteValues[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
		}

		return byteValues;
	}		
}

This code should be quite straightforward – again, consult the posts on symmetric encryption if this is new to you.

The methods in the interface can be chained together in an example as follows:

private static void TestSymmetricEncryptionService()
{
	ISymmetricCryptographyService symmetricService = new RijndaelManagedSymmetricEncryptionService();
	SymmetricKeyGenerationResult validKey = symmetricService.GenerateSymmetricKey(128);
	if (validKey.Success)
	{
		Console.WriteLine("Key used for symmetric encryption: {0}", validKey.SymmetricKey);

		SymmetricEncryptionArguments encryptionArgs = new SymmetricEncryptionArguments()
		{
			BlockSize = 128
			,CipherMode = System.Security.Cryptography.CipherMode.CBC
			,KeySize = 256
			,PaddingMode = System.Security.Cryptography.PaddingMode.ISO10126
			,PlainText = "Text to be encoded"
			,SymmetricPublicKey = validKey.SymmetricKey
		};

		SymmetricEncryptionResult encryptionResult = symmetricService.Encrypt(encryptionArgs);

		if (encryptionResult.Success)
		{
			Console.WriteLine("Cipher text from encryption: {0}", encryptionResult.CipherText);
			Console.WriteLine("Initialisation vector from encryption: {0}", encryptionResult.InitialisationVector);
			SymmetricDecryptionArguments decryptionArgs = new SymmetricDecryptionArguments()
			{
				BlockSize = encryptionArgs.BlockSize
				,CipherMode = encryptionArgs.CipherMode
				,CipherTextBase64Encoded = encryptionResult.CipherText
				,InitialisationVectorBase64Encoded = encryptionResult.InitialisationVector
				,KeySize = encryptionArgs.KeySize
				,PaddingMode = encryptionArgs.PaddingMode
				,SymmetricPublicKey = validKey.SymmetricKey
			};
			CipherTextDecryptionResult decryptionResult = symmetricService.Decrypt(decryptionArgs);
			if (decryptionResult.Success)
			{
				Console.WriteLine("Decrypted text: {0}", decryptionResult.DecodedText);
			}
			else
			{
				Console.WriteLine("Decryption failure: {0}", decryptionResult.ExceptionMessage);
			}
		}
		else
		{
			Console.WriteLine("Encryption failure: {0}", encryptionResult.ExceptionMessage);
		}
	}
	else
	{
		Console.WriteLine("Symmetric key generation failure: {0}", validKey.ExceptionMessage);
	}
}

In short we generate a valid symmetric key of 128 bits, populate the common encryption arguments, provided a plain text, encrypt it and then decrypt the cipher text using the common arguments and the IV.

In the next post we’ll look at asymmetric encryption.

View the list of posts on Architecture and Patterns here.

Externalising dependencies with Dependency Injection in .NET part 7: emailing

Introduction

In the previous post of this series we looked at how to hide the implementation of file system operations. In this post we’ll look at abstracting away emailing. Emails are often sent out in professional applications to users in specific scenarios: a new user signs up, the user places an order, the order is dispatched etc.

If you don’t know who to send emails using .NET then check the out mini-series on this page.

The first couple of posts of this series went through the process of making hard dependencies to loosely coupled ones. I won’t go through that again – you can refer back to the previous parts of this series to get an idea. You’ll find a link below to view all posts of the series. Another post on the Single Responsibility Principle includes another example of breaking out an INotificationService that you can look at.

We’ll extend the Infrastructure layer of our demo app we’ve been working on so far. So have it ready in Visual Studio and let’s get to it.

The interface

As usual we’ll need an abstraction to hide the concrete emailing logic. Emailing has potentially a considerable amount of parameters: from, to, subject, body, attachments, HTML contents, embedded resources, SMTP server and possibly many more. So the interface method(s) should accommodate all these variables somehow. One approach is to create overloads of the same method, like…

void Send(string to, string from, string subject, string body, string smtpServer);
void Send(string to, string from, string subject, string body, string smtpServer, bool isHtml);
void Send(string to, string from, string subject, string body, string smtpServer, bool isHtml, List<string> attachments);
.
.
.

…and so on including all combinations, e.g. a method with and without “isHtml”. I think this is not an optimal and future-proof interface. It is probably better to give room to all those arguments in an object and use that object as the parameter to a single method in the interface.

Also, we need to read the response of the operation. Add a new folder call Email to the Infrastructure.Common library. Insert the following object into the folder:

public class EmailSendingResult
{
	public bool EmailSentSuccessfully { get; set; }
	public string EmailSendingFailureMessage { get; set; }
}

The parameters for sending the email will be contained by an object called EmailArguments:

public class EmailArguments
{
	private string _subject;
	private string _message;
	private string _to;
	private string _from;
	private string _smtpServer;
	private bool _html;
		
	public EmailArguments(string subject, string message, string to, string from, string smtpServer, bool html)
	{
		if (string.IsNullOrEmpty(subject))
			throw new ArgumentNullException("Email subject");
		if (string.IsNullOrEmpty(message))
			throw new ArgumentNullException("Email message");
		if (string.IsNullOrEmpty(to))
			throw new ArgumentNullException("Email recipient");
		if (string.IsNullOrEmpty(from))
			throw new ArgumentNullException("Email sender");
		if (string.IsNullOrEmpty(smtpServer))
			throw new ArgumentNullException("Smtp server");
		this._from = from;
		this._message = message;
		this._smtpServer = smtpServer;
		this._subject = subject;
		this._to = to;
		this._html = html;
	}

	public List EmbeddedResources { get; set; }

	public string To
	{
		get
		{
			return this._to;
		}
	}

	public string From
	{
		get
		{
			return this._from;
		}
	}

	public string Subject
	{
		get
		{
			return this._subject;
		}
	}

	public string SmtpServer
	{
		get
		{
			return this._smtpServer;
		}
	}

	public string Message
	{
		get
		{
			return this._message;
		}
	}

	public bool Html
	{
		get
		{
			return this._html;
		}
	}
}

…where EmbeddedEmailResource is a new object, we’ll show it in a second.

So 6 parameters are made compulsory: to, from, subject, smtpServer, message body and whether the message is HTML. We can probably make this list shorter by excluding the subject and isHtml parameters but it’s a good start.

Embedded email resources come in many different forms. Insert the following enum in the Email folder:

public enum EmbeddedEmailResourceType
{
	Jpg
	, Gif
	, Tiff
	, Html
	, Plain
	, RichText
	, Xml
	, OctetStream
	, Pdf
	, Rtf
	, Soap
	, Zip
}

Embedded resources will be represented by the EmbeddedEmailResource object:

public class EmbeddedEmailResource
{
	public EmbeddedEmailResource(Stream resourceStream, EmbeddedEmailResourceType resourceType
		, string embeddedResourceContentId)
	{
		if (resourceStream == null) throw new ArgumentNullException("Resource stream");
		if (String.IsNullOrEmpty(embeddedResourceContentId)) throw new ArgumentNullException("Resource content id");
		ResourceStream = resourceStream;
		ResourceType = resourceType;
		EmbeddedResourceContentId = embeddedResourceContentId;
	}

	public Stream ResourceStream { get; set; }
	public EmbeddedEmailResourceType ResourceType { get; set; }
	public string EmbeddedResourceContentId { get; set; }
}

If you’re familiar with how to add embedded resources to an email then you’ll know why we need a Stream and a content id.

The solution should compile at this stage.

We’re now ready for the great finale, i.e. the IEmailService interface:

public interface IEmailService
{
	EmailSendingResult SendEmail(EmailArguments emailArguments);
}

Both the return type and the single parameter are custom objects that can be extended without breaking the code for any existing callers. You can add new public getters and setters to EmailArguments instead of creating the 100th different version of SendEmail in the version with primitive parameters.

Implementation

We’ll of course use the default emailing techniques built into .NET to implement IEmailService. Add a new class called SystemNetEmailService to the Email folder:

public EmailSendingResult SendEmail(EmailArguments emailArguments)
{
	EmailSendingResult sendResult = new EmailSendingResult();
	sendResult.EmailSendingFailureMessage = string.Empty;
	try
	{
		MailMessage mailMessage = new MailMessage(emailArguments.From, emailArguments.To);
		mailMessage.Subject = emailArguments.Subject;
		mailMessage.Body = emailArguments.Message;
		mailMessage.IsBodyHtml = emailArguments.Html;
		SmtpClient client = new SmtpClient(emailArguments.SmtpServer);

		if (emailArguments.EmbeddedResources != null && emailArguments.EmbeddedResources.Count > 0)
		{
			AlternateView avHtml = AlternateView.CreateAlternateViewFromString(emailArguments.Message, Encoding.UTF8, MediaTypeNames.Text.Html);
			foreach (EmbeddedEmailResource resource in emailArguments.EmbeddedResources)
			{
				LinkedResource linkedResource = new LinkedResource(resource.ResourceStream, resource.ResourceType.ToSystemNetResourceType());
				linkedResource.ContentId = resource.EmbeddedResourceContentId;
				avHtml.LinkedResources.Add(linkedResource);
			}
			mailMessage.AlternateViews.Add(avHtml);
		}

		client.Send(mailMessage);
		sendResult.EmailSentSuccessfully = true;
	}
	catch (Exception ex)
	{
		sendResult.EmailSendingFailureMessage = ex.Message;
	}

	return sendResult;
}

The code won’t compile due to the extension method ToSystemNetResourceType(). The LinkedResource object cannot work with our EmbeddedEmailResourceType type directly. It needs a string instead like “text/plain” or “application/soap+xml”. Those values are in turn stored within the MediaTypeNames object.

Add the following static class to the Email folder:

public static class EmailExtensions
{
	public static string ToSystemNetResourceType(this EmbeddedEmailResourceType resourceTypeEnum)
	{
		string type = MediaTypeNames.Text.Plain;
		switch (resourceTypeEnum)
		{
			case EmbeddedEmailResourceType.Gif:
				type = MediaTypeNames.Image.Gif;
				break;
			case EmbeddedEmailResourceType.Jpg:
				type = MediaTypeNames.Image.Jpeg;
				break;
			case EmbeddedEmailResourceType.Html:
				type = MediaTypeNames.Text.Html;
				break;
			case EmbeddedEmailResourceType.OctetStream:
				type = MediaTypeNames.Application.Octet;
				break;
			case EmbeddedEmailResourceType.Pdf:
				type = MediaTypeNames.Application.Pdf;
				break;
			case EmbeddedEmailResourceType.Plain:
				type = MediaTypeNames.Text.Plain;
				break;
			case EmbeddedEmailResourceType.RichText:
				type = MediaTypeNames.Text.RichText;
				break;
			case EmbeddedEmailResourceType.Rtf:
				type = MediaTypeNames.Application.Rtf;
				break;
			case EmbeddedEmailResourceType.Soap:
				type = MediaTypeNames.Application.Soap;
				break;
			case EmbeddedEmailResourceType.Tiff:
				type = MediaTypeNames.Image.Tiff;
				break;
			case EmbeddedEmailResourceType.Xml:
				type = MediaTypeNames.Text.Xml;
				break;
			case EmbeddedEmailResourceType.Zip:
				type = MediaTypeNames.Application.Zip;
				break;
		}

		return type;
	}
}

This is a simple converter extension to transform our custom enumeration to media type strings.

There you are, this is a good starting point to cover the email purposes in your project.

In the next part of this series we’ll look at hiding the cryptography logic or our application.

View the list of posts on Architecture and Patterns here.

Externalising dependencies with Dependency Injection in .NET part 6: file system

Introduction

In the previous post we looked at logging with log4net and saw how easy it was to switch from one logging strategy to another. By now you probably understand why it can be advantageous to remove hard dependencies from your classes: flexibility, testability, SOLID and more.

The steps to factor out your hard dependencies to abstractions usually involve the following steps:

  • Identify the hard dependencies: can the class be tested in isolation? Does the test result depend on an external object such as a web service? Can the implementation of the dependency change?
  • Identify the tasks any reasonable implementation of the dependency should be able to perform: what should a caching system do? What should any decent logging framework be able to do?
  • Build an abstraction – usually an interface – to represent those expected functions: this is so that the interface becomes as future-proof as possible. As noted before this is easier said than done as you don’t know in advance what a future implementation might need. You might need to revisit your interface and add an extra method or an extra parameter. This can be alleviated if you work with objects as parameters to the interface functions, e.g. LoggingArguments, CachingArguments – you’ll see what I mean in the next post where we’ll take up emailing
  • Inject the abstraction into the class that depends on it through one of the Dependency Injection patterns where constructor injection should be your default choice if you’re uncertain
  • The calling class will then inject a concrete implementation for the interface – alternatively you can use of the Inversion-of-control tools, like StructureMap

In this and the remaining posts of this series we won’t be dealing with the Console app in the demo anymore. The purpose of the console app was to show the goal of abstractions and dependency injection through examples. We’ve seen enough of that so we can instead concentrate on building the infrastructure layer. So open the demo solution in VS let’s add file system operations to Infrastructure.Common.

File system

.NET has an excellent built-in library for anything you’d like to do with files and directories. In the previous post on log4net we saw an example of checking if a file exists like this:

FileInfo log4netSettingsFileInfo = new FileInfo(_contextService.GetContextualFullFilePath(_log4netConfigFileName));
if (!log4netSettingsFileInfo.Exists)
{
	throw new ApplicationException(string.Concat("Log4net settings file ", _log4netConfigFileName, " not found."));
}

You can have File.WriteAllText, File.ReadAllBytes, File.Copy etc. directly in your code and you may not think that it’s a real dependency. It’s admittedly very unlikely that you don’t want to use the built-in features of .NET for file system operations and instead take some other library. So the argument of “flexibility” might not play a big role here.

However, unit testing with TDD shows that you shouldn’t make the outcome of your test depend on external elements, such as the existence of a file if the method being tested wants in fact to perform some operation on a physical file. Instead, you should be able to declare the outcome of those operations through TDD tools such as Moq which is discussed in the TDD series referred to in the previous sentence. If you see that you must create a specific file before a test is run and delete it afterwards then it’s a brittle unit test. Most real-life business applications are auto-tested by test runners in continuous integration (CI) systems such as TeamCity or Jenkins. In that case you’ll need to create the same file on the CI server(s) as well so that the unit test passes.

Therefore it still makes sense to factor out the file related stuff from your consuming classes.

The abstraction

File system operations have many facets: reading, writing, updating, deleting, copying, creating files and much more. Therefore a single file system interface is going to be relatively large. Alternatively you can break out the functions to separate interfaces such as IFileReaderService, IFileWriterService, IFileInformationService etc. You can also have a separate interface for directory-specific operations such as creating a new folder or reading the drive name.

Here we’ll start with out easy. Add a new folder called FileOperations to the Infrastructure.Common C# library. Insert an interface called IFileService:

public interface IFileService
{		
	bool FileExists(string fileFullPath);		
	long LastModifiedDateUnix(string fileFullPath);		
	string RetrieveContentsAsBase64String(string fileFullPath);		
	byte[] ReadContentsOfFile(string fileFullPath);		
	string GetFileName(string fullFilePath);		
	bool SaveFileContents(string fileFullPath, byte[] contents);		
	bool SaveFileContents(string fileFullPath, string base64Contents);			
	string GetFileExtension(string fileName);		
	bool DeleteFile(string fileFullPath);
}

That should be enough for starters.

The implementation

We’ll of course use the standard capabilities in .NET to implement the file operations. Add a new class called DefaultFileService to the FileOperations folder:

public class DefaultFileService : IFileService
{
	public bool FileExists(string fileFullPath)
	{
		FileInfo fileInfo = new FileInfo(fileFullPath);
		return fileInfo.Exists;
	}

	public long LastModifiedDateUnix(string fileFullPath)
	{
		FileInfo fileInfo = new FileInfo(fileFullPath);
		if (fileInfo.Exists)
		{
			DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0);
			TimeSpan timeSpan = fileInfo.LastWriteTimeUtc - epoch;
			return Convert.ToInt64(timeSpan.TotalMilliseconds);
		}

		return -1;
	}

	public string RetrieveContentsAsBase64String(string fileFullPath)
	{
		byte[] contents = ReadContentsOfFile(fileFullPath);
		if (contents != null)
		{
			return Convert.ToBase64String(contents);
		}
		return string.Empty;
	}

	public byte[] ReadContentsOfFile(string fileFullPath)
	{
		FileInfo fi = new FileInfo(fileFullPath);
		if (fi.Exists)
		{
			return File.ReadAllBytes(fileFullPath);
		}

		return null;
	}

	public string GetFileName(string fullFilePath)
	{
		FileInfo fi = new FileInfo(fullFilePath);
		return fi.Name;
	}

	public bool SaveFileContents(string fileFullPath, byte[] contents)
	{
		try
		{
			File.WriteAllBytes(fileFullPath, contents);
			return true;
		}
		catch
		{
			return false;
		}
	}

	public bool SaveFileContents(string fileFullPath, string base64Contents)
	{
		return SaveFileContents(fileFullPath, Convert.FromBase64String(base64Contents));
	}

	public string GetFileExtension(string fileName)
	{
		FileInfo fi = new FileInfo(fileName);
		return fi.Extension;
	}

	public bool DeleteFile(string fileFullPath)
	{
		FileInfo fi = new FileInfo(fileFullPath);
		if (fi.Exists)
		{
			File.Delete(fileFullPath);
		}

		return true;
	}
}

There you have it. Feel free to break out the file system functions to separate interfaces as suggested above.

The next post in this series will take up emailing.

View the list of posts on Architecture and Patterns here.

Externalising dependencies with Dependency Injection in .NET part 5: logging with log4net

Introduction

In the previous post we looked at how to hide the concrete implementation of the logging technology behind an abstraction. In fact we reached the original goal of showing how to rid the code of logging logic or at least how to make the code less dependent on it.

In the posts on caching and configuration we also looked at some real implementations of the abstractions that you can readily use in your project. However, with logging we only provided a simple Console based logging which is far from realistic in any non-trivial application. Therefore I’ve decided to extend the discussion on logging with a real powerhouse: log4net by Apache.

Log4net is a well-established, general purpose and widely used logging framework for .NET. You can set it up to send logging messages to multiple targets: console, file, database, a web service etc. In this post we’ll look at how to log to a file using log4net.

In a real-life large web-based application you would likely log to at least 2 sources: a file or a database and another, more advanced tool which helps you search among the messages in an efficient way. An example of such a tool is GrayLog, a web-based application where you can set up your channels and make very quick and efficient searches to track your messages.

The primary source of investigation in case of exception tracking will be this advanced tool. However, as in the case of GrayLog it may be down in which case the log messages are lost. As a backup you can then read the log messages from the log file. As mentioned above, we’ll be looking into file-based logging but if you’re looking for a more professional tool then I can recommend GrayLog.

NB: I’m not going to go through log4net specific details too much so be prepared to do your own search in some areas. A full description and demo of log4net would deserve its own series which is out of bounds in this case. However, the goal is to provide code that you can readily use in your project without much modification.

We’ll build upon the CommonInfrastructureDemo project we’ve been working with so far so have it open in Visual Studio.

Some basics

Let’s go through some preparations first. The log4net library is available through NuGet. Add the following NuGet package to the Infrastructure.Common project:

log4net NuGet

By the time you read this post the version may be higher but hopefully it won’t have any breaking changes.

As mentioned above, log4net can be configured to send the log messages to a variety of sources. Log4net will have one or more so-called appenders that will “append” the message to the defined source. Log4net can be configured in code or via a separate configuration file. The advantage of a configuration file is that you can modify the values on the deploy server without re-deploying the application. There are numerous examples on the internet showing snippets of log4net configurations. A very good starting point is the documentation on the log4net homepage available here.

In our case we’ll go for the RollingFileAppender. If the log file exceeds a certain limit then the oldest messages are erased to make room for the new ones. Add an Application Configuration File file called log4net.config to the root of the Console app, i.e. to the same level as the app.config file. Erase any default content in log4net.config and instead add the following XML content:

<?xml version="1.0"?>
<log4net>
	<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
		<file value="log.xml"/>
		<threshold value="INFO" />
		<appendToFile value="true" />
		<rollingStyle value="Size" />
		<maxSizeRollBackups value="30" />
		<maximumFileSize value="30MB" />
		<staticLogFileName value="true" />
		<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
		<layout type="log4net.Layout.XMLLayout" />
	</appender>

	<root>
		<level value="ALL" />
		<!-- Value of priority may be ALL, DEBUG, INFO, WARN, ERROR, FATAL, OFF -->
		<appender-ref ref="RollingFileAppender"/>
	</root>
</log4net>

You can find the full documentation of the rolling file appender here and here. The main thing to note that we want to log to a file called log.xml. In Solution Explorer right-click log4net.config and select Properties. Locate the “Copy to Output Directory” in the Properties window and select “Copy always”. This will put the config file to the bin folder when the application is compiled.

We’ll need to store the name of the log4net config file name in the application configuration file which we added in the post on configuration referred to above. Add the following app setting to app.config:

<add key="Log4NetSettingsFile" value="log4net.config"/>

The log4net implementation of ILoggingService will therefore need an IConfigurationRepository we saw before. It’s good that we have an implementation of IConfigurationRepository that reads from the app.config file so we’ll be able to use it.

However, we need something more. Whenever you’re trying to track down what exactly went wrong in the application based on a series of log messages you’ll need all sorts of contextual information: the user, the session, the user agent, the referrer, the exact version of the browser, the requested URL etc. Add a new folder to Infrastructure.Common called ContextProvider and insert an interface called IContextService into it:

public interface IContextService
{
	string GetContextualFullFilePath(string fileName);
	string GetUserName();
	ContextProperties GetContextProperties();
}

GetContextualFullFilePath will help us find the full path to a physical file after the deployment of the application. In our case we want to be able to find log4net.config. GetUserName is probably self-explanatory. All other context properties will be stored in the ContextProperties object. Add the following class to the ContextProvider folder:

public class ContextProperties
{
	private string _notAvailable = "N/A";

	public ContextProperties()
	{
		UserAgent = _notAvailable;
		RemoteHost = _notAvailable;
		Path = _notAvailable;
		Query = _notAvailable;
		Referrer = _notAvailable;
		RequestId = _notAvailable;
		SessionId = _notAvailable;
	}

	public string UserAgent { get; set; }
	public string RemoteHost { get; set; }
	public string Path { get; set; }
	public string Query { get; set; }
	public string Referrer { get; set; }
	public string RequestId { get; set; }
	public string SessionId { get; set; }
	public string Method { get; set; }
}

In a web-based application with a valid HttpContext object we can have the following implementation. Add the following class to the ContextProvider folder:

public class HttpContextService : IContextService
{
	public HttpContextService()
	{
		if (HttpContext.Current == null)
		{
			throw new ArgumentException("There's no available Http context.");
		}
	}

	public string GetContextualFullFilePath(string fileName)
	{
		return HttpContext.Current.Server.MapPath(string.Concat("~/", fileName));
	}

	public string GetUserName()
	{
		string userName = "<null>";
		try
		{
			if (HttpContext.Current != null && HttpContext.Current.User != null)
			{
				userName = (HttpContext.Current.User.Identity.IsAuthenticated
								? HttpContext.Current.User.Identity.Name
								: "<null>");
			}
		}
		catch
		{
		}
		return userName;
	}

	public ContextProperties GetContextProperties()
	{
		ContextProperties props = new ContextProperties();
		if (HttpContext.Current != null)
		{
			HttpRequest request = null;
			try
			{
				request = HttpContext.Current.Request;
			}
			catch (HttpException)
			{
			}
         		if (request != null)
			{
				props.UserAgent = request.Browser == null ? "" : request.Browser.Browser;
				props.RemoteHost = request.ServerVariables == null ? "" : request.ServerVariables["REMOTE_HOST"];
				props.Path = request.Url == null ? "" : request.Url.AbsolutePath;
				props.Query = request.Url == null ? "" : request.Url.Query;
				props.Referrer = request.UrlReferrer == null ? "" : request.UrlReferrer.ToString();
				props.Method = request.HttpMethod;
			}

			IDictionary items = HttpContext.Current.Items;
			if (items != null)
			{
				var requestId = items["RequestId"];
				if (requestId != null)
				{
					props.RequestId = items["RequestId"].ToString();
				}
			}

			var session = HttpContext.Current.Session;
			if (session != null)
			{
				var sessionId = session["SessionId"];
				if (sessionId != null)
				{
					props.SessionId = session["SessionId"].ToString();
				}
			}
		}

		return props;
	}
}

Most of this code is about extracting various data from the HTTP request/context.

In a non-HTTP based application we’ll go for a simpler implementation. Add a class called ThreadContextService to the ContextProvider folder:

public class ThreadContextService : IContextService
{
	public string GetContextualFullFilePath(string fileName)
	{
		string dir = Directory.GetCurrentDirectory();
		FileInfo resourceFileInfo = new FileInfo(Path.Combine(dir, fileName));
		return resourceFileInfo.FullName;
	}

	public string GetUserName()
	{
		string userName = "<null>";
		try
		{
			if (Thread.CurrentPrincipal != null)
			{
				userName = (Thread.CurrentPrincipal.Identity.IsAuthenticated
								? Thread.CurrentPrincipal.Identity.Name
								: "<null>");
			}
		}
		catch
		{
		}
		return userName;
	}

	public ContextProperties GetContextProperties()
	{
		return new ContextProperties();
	}
}

Now we have all the ingredients for the log4net implementation if ILoggingService. Add the following class called Log4NetLoggingService to the Logging folder:

public class Log4NetLoggingService : ILoggingService
{
	private readonly IConfigurationRepository _configurationRepository;
	private readonly IContextService _contextService;
	private string _log4netConfigFileName;

	public Log4NetLoggingService(IConfigurationRepository configurationRepository, IContextService contextService)
	{
		if (configurationRepository == null) throw new ArgumentNullException("ConfigurationRepository");
		if (contextService == null) throw new ArgumentNullException("ContextService");
		_configurationRepository = configurationRepository;
		_contextService = contextService;
		_log4netConfigFileName = _configurationRepository.GetConfigurationValue<string>("Log4NetSettingsFile");
		if (string.IsNullOrEmpty(_log4netConfigFileName))
		{
			throw new ApplicationException("Log4net settings file missing from the configuration source.");
		}
		SetupLogger();
	}

	private void SetupLogger()
	{
		FileInfo log4netSettingsFileInfo = new FileInfo(_contextService.GetContextualFullFilePath(_log4netConfigFileName));
		if (!log4netSettingsFileInfo.Exists)
		{
			throw new ApplicationException(string.Concat("Log4net settings file ", _log4netConfigFileName, " not found."));
		}
		log4net.Config.XmlConfigurator
			.ConfigureAndWatch(log4netSettingsFileInfo);
	}

	public void LogInfo(object logSource, string message, Exception exception = null)
	{
		LogMessageWithProperties(logSource, message, Level.Info, exception);
	}

	public void LogWarning(object logSource, string message, Exception exception = null)
	{
		LogMessageWithProperties(logSource, message, Level.Warn, exception);
	}

	public void LogError(object logSource, string message, Exception exception = null)
	{
		LogMessageWithProperties(logSource, message, Level.Error, exception);
	}

	public void LogFatal(object logSource, string message, Exception exception = null)
	{
		LogMessageWithProperties(logSource, message, Level.Fatal, exception);
	}

	private void LogMessageWithProperties(object logSource, string message, Level level, Exception exception)
	{
		var logger = LogManager.GetLogger(logSource.GetType());
			
		var loggingEvent = new LoggingEvent(logSource.GetType(), logger.Logger.Repository, logger.Logger.Name, level, message, null);
		AddProperties(logSource, exception, loggingEvent);
		try
		{
			logger.Logger.Log(loggingEvent);				
		}
		catch (AggregateException ae)
		{
			ae.Handle(x => { return true; });
		}
		catch (Exception) { }
	}

	private string GetUserName()
	{
		return _contextService.GetUserName();
	}

	private void AddProperties(object logSource, Exception exception, LoggingEvent loggingEvent)
	{
		loggingEvent.Properties["UserName"] = GetUserName();
		try
		{
			ContextProperties contextProperties = _contextService.GetContextProperties();
			if (contextProperties != null)
			{
				try
				{						
					loggingEvent.Properties["UserAgent"] = contextProperties.UserAgent;
					loggingEvent.Properties["RemoteHost"] = contextProperties.RemoteHost;
					loggingEvent.Properties["Path"] = contextProperties.Path;
					loggingEvent.Properties["Query"] = contextProperties.Query;
					loggingEvent.Properties["RefererUrl"] = contextProperties.Referrer;
					loggingEvent.Properties["RequestId"] = contextProperties.RequestId;
					loggingEvent.Properties["SessionId"] = contextProperties.SessionId;
				}
				catch (Exception)
				{
				}
			}
				
			loggingEvent.Properties["ExceptionType"] = exception == null ? "" : exception.GetType().ToString();
			loggingEvent.Properties["ExceptionMessage"] = exception == null ? "" : exception.Message;
			loggingEvent.Properties["ExceptionStackTrace"] = exception == null ? "" : exception.StackTrace;
			loggingEvent.Properties["LogSource"] = logSource.GetType().ToString();
		}
		catch (Exception ex)
		{
			var type = typeof(Log4NetLoggingService);
			var logger = LogManager.GetLogger(type);
			logger.Logger.Log(type, Level.Fatal, "Exception when extracting properties: " + ex.Message, ex);
		}
	}		
}

We inject two dependencies by way of constructor injection: IConfigurationRepository and IContextService. IConfigurationRepository will help us locate the name of the log4net configuration file. IContextService will provide contextual data to the log message. Log4net is set up in SetupLogger(). We check for the existence of the config file. We then call log4net.Config.XmlConfigurator.ConfigureAndWatch to let log4net read the configuration settings from an XML file and watch for any changes.

The implemented LogX messages all call upon LogMessageWithProperties where we simply wrap the log message in a LoggingEvent object along with all contextual data.

Originally we had the below code to instantiate a logged and cached ProductService to log to the console:

IProductService productService = new ProductService(new ProductRepository(), new ConfigFileConfigurationRepository());
IProductService cachedProductService = new CachedProductService(productService, new SystemRuntimeCacheStorage());
IProductService loggedCachedProductService = new LoggedProductService(cachedProductService, new ConsoleLoggingService());

We can easily change the logging mechanism by simply injecting the log4net implementation of ILoggingService into LoggedProductService:

IConfigurationRepository configurationRepository = new ConfigFileConfigurationRepository();
IProductService productService = new ProductService(new ProductRepository(), configurationRepository);
IProductService cachedProductService = new CachedProductService(productService, new SystemRuntimeCacheStorage());
ILoggingService loggingService = new Log4NetLoggingService(configurationRepository, new ThreadContextService());
IProductService loggedCachedProductService = new LoggedProductService(cachedProductService, loggingService);

Run Program.cs and if all went well then you’ll have a new file called log.xml in the …MyCompany.ProductConsole/bin/Debug folder with some XML entries. Here comes an excerpt:

<log4net:event logger="MyCompany.ProductConsole.Services.LoggedProductService" timestamp="2014-08-23T22:44:52.7591938+02:00" level="INFO" thread="9" domain="MyCompany.ProductConsole.vshost.exe" username="andras.nemes"><log4net:message>Starting GetProduct method</log4net:message><log4net:properties><log4net:data name="log4net:HostName" value="andras1" /><log4net:data name="Path" value="N/A" /><log4net:data name="SessionId" value="N/A" /><log4net:data name="log4net:UserName" value="andras.nemes" /><log4net:data name="Query" value="N/A" /><log4net:data name="ExceptionMessage" value="" /><log4net:data name="UserName" value="&lt;null&gt;" /><log4net:data name="RefererUrl" value="N/A" /><log4net:data name="LogSource" value="MyCompany.ProductConsole.Services.LoggedProductService" /><log4net:data name="RemoteHost" value="N/A" /><log4net:data name="ExceptionStackTrace" value="" /><log4net:data name="UserAgent" value="N/A" /><log4net:data name="ExceptionType" value="" /><log4net:data name="RequestId" value="N/A" /><log4net:data name="log4net:Identity" value="" /></log4net:properties></log4net:event>

So it took some time to implement the log4net version of ILoggingService but to switch from the console-based implementation was a breeze.

However, you may still see one log message logged to the console. That’s from the code line in ProductService.GetProduct:

LogProviderContext.Current.LogInfo(this, "Log message from the contextual log provider");

…where LogProviderContext returns a ConsoleLoggingService by default. However, it also allows us to set the implementation of ILoggingService. Insert the following code in Main…

LogProviderContext.Current = loggingService;

…just below…

ILoggingService loggingService = new Log4NetLoggingService(configurationRepository, new ThreadContextService());

Then you’ll see that even the previously mentioned log message ends up in log.xml:

<log4net:event logger="MyCompany.ProductConsole.Services.ProductService" timestamp="2014-08-23T22:56:19.411468+02:00" level="INFO" thread="8" domain="MyCompany.ProductConsole.vshost.exe" username="andras.nemes"><log4net:message>Log message from the contextual log provider</log4net:message><log4net:properties><log4net:data name="log4net:HostName" value="andras1" /><log4net:data name="Path" value="N/A" /><log4net:data name="SessionId" value="N/A" /><log4net:data name="log4net:UserName" value="andras.nemes" /><log4net:data name="Query" value="N/A" /><log4net:data name="ExceptionMessage" value="" /><log4net:data name="UserName" value="&lt;null&gt;" /><log4net:data name="RefererUrl" value="N/A" /><log4net:data name="LogSource" value="MyCompany.ProductConsole.Services.ProductService" /><log4net:data name="RemoteHost" value="N/A" /><log4net:data name="ExceptionStackTrace" value="" /><log4net:data name="UserAgent" value="N/A" /><log4net:data name="ExceptionType" value="" /><log4net:data name="RequestId" value="N/A" /><log4net:data name="log4net:Identity" value="" /></log4net:properties></log4net:event>

So that’s it about logging. We’ll continue with the file system in the next part.

View the list of posts on Architecture and Patterns here.

4 design patterns to learn with C# .NET

Here come 4 well-known design patterns that I think most developers will benefit from, even those that are by nature anti-designpattern. Note that the list is biased and only shows the ones that I personally use the most often in my work.

If you don’t find the solution to your design problem here then check out the full list of patterns discussed on this blog here.

  • Adapter: this pattern helps you hide the functionality of a class which is not under your control behind an abstraction
  • Strategy: this pattern will help you with cleaning up anti-SOLID code where you check for certain properties, especially the type of an object to tweak your code. You will end up with proper objects instead of brittle switch or if-else statements
  • Decorator: if you’d like to extend the functionality of a class without changing its implementation then this pattern is something to consider. You can build compound objects by nesting decorators where the individual elements are still standalone classes.
  • Factory: this pattern will help you build objects using parameters whose values are not known beforehand. E.g. if you don’t which concrete type of an abstract class to return then hide that build functionality behind an abstract factory
Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: