Examining the Assembly using Reflection in .NET

The CLR code of a project is packaged into an assembly. We can inspect a range of metadata from an assembly that also the CLR uses to load and execute runnable code: classes, methods, interfaces, enumerations etc.

In order to inspect an Assembly in a .NET project we’ll first need to get a reference to the assembly in question. There are various ways to retrieve an assembly, including:

Assembly callingAssembly = Assembly.GetCallingAssembly();
Assembly entryAssembly = Assembly.GetEntryAssembly();
Assembly executingAssembly = Assembly.GetExecutingAssembly();

…where Assembly is located in the System.Reflection namespace.

The static methods above represent the following:

  • GetCallingAssembly: to get the assembly one level up the call stack, i.e. which contains the method the current executing code
  • GetEntryAssembly: to get the assembly which contains the entry point to the application, e.g. the Main method in a Console app
  • GetExecutingAssembly: to get the assembly of the currently running code

There’s also a way to load a specific assembly using the GetAssembly(Type type) method. E.g. if you have a Customer class then you can load the assembly that contains the Customer class as follows:

Assembly specificAssembly = Assembly.GetAssembly(typeof(Customer));

Once you have the assembly you can read various metadata from it, examples:

Console.WriteLine("Full name: {0}", callingAssembly.FullName);
Console.WriteLine("Location: {0}", callingAssembly.Location);
Console.WriteLine("Loaded for reflection only? {0}", callingAssembly.ReflectionOnly);
Console.WriteLine("Loaded from GAC? {0}", callingAssembly.GlobalAssemblyCache);
Console.WriteLine(".NET Version: {0}", callingAssembly.ImageRuntimeVersion);

…which in my case outputs the following:

Basic assembly information

You may wonder what “loaded for reflection” means. You can get hold of an assembly in order to inspect its metadata without the possibility to execute any action on it. This is how we can load the assembly containing the “string” class in .NET:

Assembly reflectionOnlyAssembly = Assembly.ReflectionOnlyLoad("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");

You can execute an assembly by creating an instance of it using the CreateInstance method. However, if the assembly was only loaded for reflection then CreateInstance will throw an exception. If you know that you only want to read some metadata from an assembly but not execute it then you can use the ReflectionOnlyLoad method to save time loading it fully into the AppDomain.

View all posts on Reflection here.

Finding the user’s supported cultures using the CultureInfo class in .NET C#

The CultureInfo class has a static method to retrieve the supported locales on the user’s machine:

CultureInfo[] supportedCultures = CultureInfo.GetCultures(CultureTypes.AllCultures);

The GetCultures method accepts a CultureTypes enumeration:

  • AllCultures: show all cultures regardless of the type
  • FrameworkCultures: show all specific and neutral cultures that ship with .NET
  • InstalledWin32Cultures: all cultures installed on Windows
  • NeutralCultures: all language-specific, i.e. neutral cultures where the region which determines the specific culture is omitted
  • ReplacementCultures: custom cultures created by the user that replace an existing culture
  • SpecificCultures: the most precise culture type where both the language and regions are denoted
  • UserCustomCulture: custom cultures
  • WindowsOnlyCultures: deprecated, yields an array with 0 elements in .NET 4+

So in case you’d like to find all specific cultures and their names you can write as follows:

CultureInfo[] supportedCultures = CultureInfo.GetCultures(CultureTypes.SpecificCultures);
List<CultureInfo> ordered = supportedCultures.OrderBy(c => c.Name).ToList();
foreach (CultureInfo ci in ordered)
{
	Console.WriteLine(string.Concat(ci.Name, ": ", ci.EnglishName));
}

However, if you’re only interested in the supported languages then the following will do:

CultureInfo[] supportedCultures = CultureInfo.GetCultures(CultureTypes.NeutralCultures);
List<CultureInfo> ordered = supportedCultures.OrderBy(c => c.Name).ToList();
foreach (CultureInfo ci in ordered)
{
	Console.WriteLine(string.Concat(ci.Name, ": ", ci.EnglishName));
}

Read all posts related to Globalisation in .NET 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

Externalising dependencies with Dependency Injection in .NET part 4: logging part 1

Introduction

In this post of this series we looked at ways to remove caching logic from our ProductService class. In the previous post we saw how to hide reading from a configuration file. We’ll follow in a similar fashion in this post which takes up logging. A lot of the techniques and motivations will be re-used and not explained again.

We’ll build upon the demo app we started on so have it ready in Visual Studio.

Logging

Logging is a wide area with several questions you need to consider. What needs to be logged? Where should we save the logs? How can the log messages be correlated? We’ll definitely not answer those questions here. Instead, we’ll look at techniques to call the logging implementation.

I’d like to go through 3 different ways to put logging into your application. For the demo we’ll first send the log messages to the Console window for an easy start. After that, we’ll propose a way of implementing logging with log4net. The material is too much for a single post so I’ve decided to divide this topic into 2 posts.

Starting point

If you ever had to log something in a professional .NET project then chances are that you used Log4Net. It’s a mature and widely used logging framework. However, it’s by far not the only choice. There’s e.g. NLog and probably many more.

If you recall the first solution for the caching problem we put the caching logic directly inside ProductService.GetProduct:

public GetProductResponse GetProduct(GetProductRequest getProductRequest)
{
	GetProductResponse response = new GetProductResponse();
	try
	{
		string storageKey = "GetProductById";
		ObjectCache cache = MemoryCache.Default;
		Product p = cache.Contains(storageKey) ? (Product)cache[storageKey] : null;
		if (p == null)
		{
			p = _productRepository.FindBy(getProductRequest.Id);
			CacheItemPolicy policy = new CacheItemPolicy() { AbsoluteExpiration = DateTime.Now.AddMinutes(5) };
			cache.Add(storageKey, p, policy);
		}
		response.Product = p;
		if (p != null)
		{
			response.Success = true;
		}
		else
		{
			response.Exception = "No such product.";
		}
	}
	catch (Exception ex)
	{
		response.Exception = ex.Message;
	}
	return response;
}

We then also discussed the disadvantages of such a solution, like diminished testability and flexibility. We could follow a similar solution for the logging with log4net with direct calls from ProductService like this:

var logger = logSource.GetLogger();
var loggingEvent = new LoggingEvent(logSource.GetType(), logger.Logger.Repository, logger.Logger.Name, Level.Info, "Code starting", null);
logger.Logger.Log(loggingEvent);

…which would probably be factored out to a different method but the strong dependency on and tight coupling to log4net won’t go away. We could have similar code for NLog and other logging technologies out there. Then some day the project leader says that the selected technology must change because the one they used on a different project is much more powerful. Then the developer tasked with changing the implementation at every possible place in the code won’t have a good day out.

The logging interface

As we saw in the previous post the key step towards an OOP solution is an abstraction which hides the concrete implementation. This is not always a straightforward step. In textbooks it always looks easy to create these interfaces because they always give you a variety of concrete implementations beforehand. Then you’ll know what’s common to them and you can put those common methods in an interface. In reality, however, you’ll always need to think carefully about the features that can be common across a variety of platforms: what should every decent logging framework be able to do at a minimum? What should a caching technology be able to do? Etc., you can ask similar questions about every dependency in your code that you’re trying to factor out. The interface should find the common features and not lean towards one specific implementation so that it becomes as future-proof as possible.

Normally any logging framework should be able to log messages, message levels – e.g. information or fatal – and possibly exceptions. Insert a new folder called Logging to the Infrastructure layer and add the following interface into it:

public interface ILoggingService
{
	void LogInfo(object logSource, string message, Exception exception = null);
	void LogWarning(object logSource, string message, Exception exception = null);
	void LogError(object logSource, string message, Exception exception = null);
	void LogFatal(object logSource, string message, Exception exception = null);
}

This should be enough for starters: the source where the logging happens, the message and an exception. The level is represented by the method names.

Logging to the console

As promised above, we’ll take it easy first and send the log messages directly to the console. So add the following implementation to the Logging folder:

public class ConsoleLoggingService : ILoggingService
{
	private ConsoleColor _defaultColor = ConsoleColor.Gray;

	public void LogInfo(object logSource, string message, Exception exception = null)
	{
		Console.ForegroundColor = ConsoleColor.Green;
		Console.WriteLine(string.Concat("Info from ", logSource.ToString(), ": ", message));
		PrintException(exception);
		ResetConsoleColor();
	}

	public void LogWarning(object logSource, string message, Exception exception = null)
	{
		Console.ForegroundColor = ConsoleColor.Yellow;
		Console.WriteLine(string.Concat("Warning from ", logSource.ToString(), ": ", message));
		PrintException(exception);
		ResetConsoleColor();
	}

	public void LogError(object logSource, string message, Exception exception = null)
	{
		Console.ForegroundColor = ConsoleColor.DarkMagenta;
		Console.WriteLine(string.Concat("Error from ", logSource.ToString(), ": ", message));
		PrintException(exception);
		ResetConsoleColor();
	}

	public void LogFatal(object logSource, string message, Exception exception = null)
	{
		Console.ForegroundColor = ConsoleColor.Red;
		Console.WriteLine(string.Concat("Fatal from ", logSource.ToString(), ": ", message));
		PrintException(exception);
		ResetConsoleColor();
	}

	private void ResetConsoleColor()
	{
		Console.ForegroundColor = _defaultColor;
	}

	private void PrintException(Exception exception)
	{
		if (exception != null)
		{
			Console.WriteLine(string.Concat("Exception logged: ", exception.Message));
		}
	}
}

That should be quite straightforward I believe.

Solution 1: decorator

We saw an example of the Decorator pattern in the post on caching referred to in the intro and we’ll build on that. We’ll extend the original ProductService implementation to include both caching and logging. Add the following decorator to the Services folder of the Console app layer:

public class LoggedProductService : IProductService
{
	private readonly IProductService _productService;
	private readonly ILoggingService _loggingService;

	public LoggedProductService(IProductService productService, ILoggingService loggingService)
	{
		if (productService == null) throw new ArgumentNullException("ProductService");
		if (loggingService == null) throw new ArgumentNullException("LoggingService");
		_productService = productService;
		_loggingService = loggingService;
	}

	public GetProductResponse GetProduct(GetProductRequest getProductRequest)
	{
		GetProductResponse response = new GetProductResponse();
		_loggingService.LogInfo(this, "Starting GetProduct method");
		try
		{
			response = _productService.GetProduct(getProductRequest);
			if (response.Success)
			{
				_loggingService.LogInfo(this, "GetProduct success!!!");
			}
			else
			{
				_loggingService.LogError(this, "GetProduct failure...", new Exception(response.Exception));
			}
		}
		catch (Exception ex)
		{
			response.Exception = ex.Message;
			_loggingService.LogError(this, "Exception in GetProduct!!!", ex);
		}
		return response;
	}
}

You’ll recall the structure from the Caching decorator. We hide both the product and logging service behind interfaces. We delegate the product retrieval to the product service and logging to the logging service. So if you’d like to add logging to the original ProductService class then you can have the following code in Main:

IProductService productService = new ProductService(new ProductRepository());
IProductService loggedProductService = new LoggedProductService(productService, new ConsoleLoggingService());
GetProductResponse getProductResponse = loggedProductService.GetProduct(new GetProductRequest() { Id = 2 });
if (getProductResponse.Success)
{
	Console.WriteLine(string.Concat("Product name: ", getProductResponse.Product.Name));
}
else
{
	Console.WriteLine(getProductResponse.Exception);
}

If you run this code then you’ll see an output similar to this:

Info messages from logging service

If you run the code with a non-existent product ID then you’ll see an exception as well:

Exception from logging service

If you’d then like to add both caching and logging to the plain ProductService class then you can have the following test code:

IProductService productService = new ProductService(new ProductRepository());
IProductService cachedProductService = new CachedProductService(productService, new SystemRuntimeCacheStorage());
IProductService loggedCachedProductService = new LoggedProductService(cachedProductService, new ConsoleLoggingService());
GetProductResponse getProductResponse = loggedCachedProductService.GetProduct(new GetProductRequest() { Id = 2 });
if (getProductResponse.Success)
{
	Console.WriteLine(string.Concat("Product name: ", getProductResponse.Product.Name));
}
else
{
	Console.WriteLine(getProductResponse.Exception);
}

getProductResponse = loggedCachedProductService.GetProduct(new GetProductRequest() { Id = 2 });
			
Console.ReadKey();

Notice how we build up the compound decorator loggedCachedProductService from productService and cachedProductService in the beginning of the code. You can step through the code and you’ll see how we print the log messages, check the cache and retrieve the product from ProductService if necessary.

Solution 2: ambient context

I think the above solution with dependency injection, interfaces and decorators follows SOLID principles quite well. We can build upon the ProductService class using the decorators, pass different implementations of the abstractions and test each component independently.

As discussed in the post on Interception – check the section called Interception using the Decorator pattern – this solution can have some practical limitations, such as writing a large number of tedious code. While you may not want to do caching in every single layer, logging is different. You may want to log from just about any layer of the application: UI, services, controllers, repositories. So you may need to write a LoggedController, LoggedRepository, LoggedService etc. for any component where you want to introduce logging.

In the post on patterns in dependency injection we discussed a technique called ambient context. I suggest you read that section if you don’t understand the term otherwise you may not understand the purpose of the code below. Also, you’ll find its advantages and disadvantages there as well.

Insert the following class into the Logging folder of the infrastructure project:

public abstract class LogProviderContext
{
	private static readonly string _nameDataSlot = "LogProvider";

	public static ILoggingService Current
	{
		get
		{
			ILoggingService logProviderContext = Thread.GetData(Thread.GetNamedDataSlot(_nameDataSlot)) as ILoggingService;
			if (logProviderContext == null)
			{
				logProviderContext = LogProviderContext.DefaultLogProviderContext;
				Thread.SetData(Thread.GetNamedDataSlot(_nameDataSlot), logProviderContext);
			}
			return logProviderContext;
		}
		set
		{
			Thread.SetData(Thread.GetNamedDataSlot(_nameDataSlot), value);
		}
	}

	public static ILoggingService DefaultLogProviderContext = new ConsoleLoggingService();
}

You can call this code from any other layer which has a reference to the infrastructure layer. E.g. you can have the following code directly in ProductService.GetProducts:

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

Solution 3: injecting logging into ProductService

Another solution which lies somewhere between ambient context and the decorator is having ProductService depend upon an ILoggingService:

public ProductService(IProductRepository productRepository, ILoggingService loggingService)

You can then use the injected ILoggingService to log you messages. We saw a similar example in the post on caching and also in the post on Interception referred to above. Read the section called Dependency injection in the post on Interception to read more why this approach might be good or bad.

However, if you’re faced with such a constructor and still want to test ProductService in isolation you can use a Null Object version of ILoggingService. Add the following class to the Logging folder:

public class NoLogService : ILoggingService
{
	public void LogInfo(object logSource, string message, Exception exception = null)
	{}

	public void LogWarning(object logSource, string message, Exception exception = null)
	{}

	public void LogError(object logSource, string message, Exception exception = null)
	{}

	public void LogFatal(object logSource, string message, Exception exception = null)
	{}
}

In the next post we’ll continue with logging to show a more realistic implementation of ILoggingService with log4net.

View the list of posts on Architecture and Patterns here.

How to send emails in .NET part 11: credentials

In the previous part in this mini-series we looked at how to handle exceptions. We’ll briefly look at authentication in this post.

Some SMTP servers require you to provide a username and password in order to send emails.

You can set the default network credentials as follows:

string smtpServer = "mail.blahblah.com";
SmtpClient client = new SmtpClient(smtpServer);
client.UseDefaultCredentials = true;

Another way to achieve the same is the following:

client.Credentials = CredentialCache.DefaultNetworkCredentials;

…where CredentialCache is found in the System.Net namespace.

You can also set the username and password manually as follows:

client.Credentials = new NetworkCredential("name", "password");

Read all posts related to emailing in .NET here.

How to send emails in .NET part 10: handling exceptions

In this mini series on emailing in .NET we saw how to compose and send emails.

There are many things that can go wrong when you send an email: the SMTP server is down, your credentials are invalid, the recipient is invalid etc. When sending an email in .NET you should always catch and handle any SmtpExceptions and SmtpFailedRecipientExceptions. Examples:

SmtpClient client = new SmtpClient(null);
try
{
	client.Send(mailMessage);
}
catch (InvalidOperationException invalid)
{
	Console.WriteLine("Server hostname is missing: " + invalid.Message);
}

InvalidOperationException is thrown in case the host is missing.

string smtpServer = "mail.blahblah.com";
SmtpClient client = new SmtpClient(smtpServer);
try
{
	client.Send(mailMessage);
}
catch (SmtpException smtpNotFound)
{
	Console.WriteLine("Server hostname is invalid: " + smtpNotFound.Message);
}

SmtpClient will try to contact mail.blahblah.com but fails of course. If you run this code then be patient as the default timeout of SmtpClient is 100 seconds, so you won’t see the exception message immediately.

In the above case SmtpException will have an inner exception of type WebException. smtpNotFound.Message like above will only show a generic message: “Failure sending mail”. If you want to dig deeper you’ll need to check if there’s any inner exception:

string smtpServer = "mail.blahblah.com";
SmtpClient client = new SmtpClient(smtpServer);
try
{
	client.Send(mailMessage);
}
catch (SmtpException smtpNotFound)
{
	Console.WriteLine("Server hostname is invalid: " + smtpNotFound.Message);
        if (smtpNotFound.InnerException != null)
	{
		Console.WriteLine(smtpNotFound.InnerException.Message);
	}
}

The inner exception message will be “Unable to connect to the remote server”.

SmtpException can also be thrown if the operation times out, but in that case there will be no inner exceptions:

SmtpClient client = new SmtpClient(smtpServer);
client.Timeout = 10;
try
{
	client.Send(mailMessage);
}
catch (SmtpException smtpNotFound)
{
	Console.WriteLine("Server hostname is invalid: " + smtpNotFound.Message);
}

We set the timeout to 10 seconds which is reached before SmtpClient determines that the SMTP server cannot be reached. Hence the exception message will say “The operation has timed out.”

SmtpException can also be thrown for a variety of other reasons like message transmission problems, so don’t forget to check the inner exception as well, it may contain a more detailed description of the problems.

There’s also an exception of type SmtpFailedRecipientException. If you work at GreatCompany Ltd. and your mail server is mail.greatcompany.com and you want to send an email to john@greatcompany.com then your mail server will be able to determine if the recipient exists and return an error if it doesn’t. If however, you’re sending an email to a recipient on another mail server then mail.greatcompany.com won’t of course see whether the recipient is valid or not. So you can only rely on this exception type if you’re sending an email within your network.

In this post we saw how to send an email asynchronously through SendAsync. The event arguments to the SendCompleted event handler includes a property called Error. If you send your email in this manner than the InvalidOperationException and SmtpException error caught will be set to this property so you can check the result.

Read all posts related to emailing in .NET here.

Externalising dependencies with Dependency Injection in .NET part 3: configuration

Introduction

In the previous post of this series we looked at how to remove the caching logic from a class and hide it behind an interface. We’ll follow in a similar fashion in this post which takes up reading settings from a configuration source.

We’ll build upon the demo we’ve been working on so far so have it ready in Visual Studio.

Configuration and settings

Probably every even vaguely serious application out there has some configuration store. The configuration values contain the settings that are necessary so that the app can function normally. A good example from .NET is the web.config or app.config file with their appSettings section:

<appSettings>
    <add key="Environment" value="Alpha" />
    <add key="MinimumLimit" value="1000" />
    <add key="Storage" value="mongo" />
</appSettings>

In Java you’d put these settings into a .properties file. The configuration files will allow you to change the behaviour of your app without recompiling and redeploying them.

You can of course store your settings in other sources: a database, a web service or some other mechanism. Hence the motivations for hiding the concrete technology behind an abstraction are similar to those listed in the post on caching: testability, flexibility, SOLID.

The abstraction

Application settings are generally key-value pairs so constructing an interface for a settings storage is fairly simple and the below example is one option. The key is almost always a string and the value can be any primitive or a string. Add a new folder called Configuration to the infrastructure layer. Add the following interface to it:

public interface IConfigurationRepository
{
	T GetConfigurationValue<T>(string key);
	T GetConfigurationValue<T>(string key, T defaultValue);
}

The implementation

One obvious implementation is to read the values from app.config/web.config using the ConfigurationManager class in the System.Configuration library. Add this dll to the references list of the infrastructure layer.

Then insert the following implementation of the interface to the Configuration folder:

public class ConfigFileConfigurationRepository : IConfigurationRepository
{
	public T GetConfigurationValue<T>(string key)
	{
		string value = ConfigurationManager.AppSettings[key];
		if (value == null)
		{
			throw new KeyNotFoundException("Key " + key + " not found.");
		}
		try
		{
			if (typeof(Enum).IsAssignableFrom(typeof(T)))
				return (T)Enum.Parse(typeof(T), value);
			return (T)Convert.ChangeType(value, typeof(T));
		}
		catch (Exception ex)
		{
			throw ex;
		}
	}

	public T GetConfigurationValue<T>(string key, T defaultValue)
	{
		string value = ConfigurationManager.AppSettings[key];
		if (value == null)
		{
			return defaultValue;
		}
		try
		{
			if (typeof(Enum).IsAssignableFrom(typeof(T)))
				return (T)Enum.Parse(typeof(T), value);
			return (T)Convert.ChangeType(value, typeof(T));
		}
		catch (Exception ex)
		{
			return defaultValue;
        	}
	}
}

The console app has an app.config file by default. If not then you’ll need to add an Application Configuration File as it’s called in the Add New Item dialog. We’ll simulate a test scenario where it’s possible to return a default product instead of consulting the data store. Add the following appSettings section with the configuration tags of app.config:

<appSettings>
	<add key="returnDefaultProduct" value="true"/>
	<add key="defaultProductId" value="99"/>
	<add key="defaultProductName" value="DEFAULT"/>
	<add key="defaultProductQuantity" value="1000"/>
</appSettings>

Next we’ll inject the dependency through the constructor of ProductService. Modify the ProductService private fields and constructor as follows:

private readonly IProductRepository _productRepository;
private readonly IConfigurationRepository _configurationRepository;

public ProductService(IProductRepository productRepository, IConfigurationRepository configurationRepository)
{
	if (productRepository == null) throw new ArgumentNullException("ProductRepository");
	if (configurationRepository == null) throw new ArgumentNullException("ConfigurationRepository");
	_productRepository = productRepository;
	_configurationRepository = configurationRepository;
}

This breaks of course the existing code which constructs a ProductService. Modify it as follows:

IProductService productService = new ProductService(new ProductRepository(), new ConfigFileConfigurationRepository());

The modify ProductService.GetProduct as follows to use the configuration repository:

public GetProductResponse GetProduct(GetProductRequest getProductRequest)
{
	GetProductResponse response = new GetProductResponse();
	try
	{
		bool returnDefault = _configurationRepository.GetConfigurationValue<bool>("returnDefaultProduct", false);
		Product p = returnDefault ? BuildDefaultProduct() : _productRepository.FindBy(getProductRequest.Id);
		response.Product = p;
		if (p != null)
		{
			response.Success = true;
		}
		else
		{
			response.Exception = "No such product.";
		}
	}
	catch (Exception ex)
	{
		response.Exception = ex.Message;
	}
	return response;
}

private Product BuildDefaultProduct()
{
	Product defaultProduct = new Product();
	defaultProduct.Id = _configurationRepository.GetConfigurationValue<int>("defaultProductId");
	defaultProduct.Name = _configurationRepository.GetConfigurationValue<string>("defaultProductName");
	defaultProduct.OnStock = _configurationRepository.GetConfigurationValue<int>("defaultProductQuantity");
	return defaultProduct;
}

Run Main and you’ll see that the configuration values are indeed correctly retrieved from app.config. A possible improvement here is to put all configuration keys into a separate container class or a resource file instead of putting them here directly. Then you could retrieve them by writing something like this:

defaultProduct.Id = _configurationRepository.GetConfigurationValue<int>(ConfigurationKeys.DefaultProductId);

In the next post we’ll look at logging.

View the list of posts on Architecture and Patterns here.

How to send emails in .NET part 9: sending emails

Up to now in this mini series we’ve looked at how to compose email messages. The next step is to actually send them to the recipient(s).

We saw that the easiest way to send a message is using the SmtpClient.Send method:

string from = "andras.nemes@company.com";
string to = "john.smith@company.com";
string subject = "This is the subject";
string plainTextBody = "This is a great message.";
MailMessage mailMessage = new MailMessage(from, to, subject, plainTextBody);
string smtpServer = "mail.company.com";
SmtpClient client = new SmtpClient(smtpServer);
client.Send(mailMessage);

The Send method is a “normal” synchronous method, i.e. it will block the code execution until the email has been sent.

In case you’d like to send the message asynchronously you have at least 2 options. If you have a .NET 4.5 project then you can use the awaitable version of Send, which is SendMailAsync. You don’t know what await-async means? Start here.

The updated code looks like this then:

public async Task SendMessageAsync()
{
	string from = "andras.nemes@company.com";
        string to = "john.smith@company.com";
        string subject = "This is the subject";
        string plainTextBody = "This is a great message.";
        MailMessage mailMessage = new MailMessage(from, to, subject, plainTextBody);
        string smtpServer = "mail.company.com";
        SmtpClient client = new SmtpClient(smtpServer);
	await client.SendMailAsync(mailMessage);
}

You’d then call the method as follows:

await SendMessageAsync();

Another option is to use the SendAsync method of SmtpClient and hook up to its SendCompleted event:

SmtpClient client = new SmtpClient(smtpServer);
client.SendCompleted += client_SendCompleted;
client.SendAsync(mailMessage, "Hello world");	

…where client_SendCompleted is an event handler:

void client_SendCompleted(object sender, AsyncCompletedEventArgs e)
{
	Console.WriteLine("Cancelled? " + e.Cancelled);
	Console.WriteLine("Error? " + (e.Error == null));
	Console.WriteLine("User specified arg " + (e.UserState == null ? "none" : e.UserState.ToString()));
}

…which is called when the sending operation has completed. You can then read if the sending operation has been cancelled, if there are any exceptions and if there are any custom arguments in the UserState object. You can supply your own object here, any object, through the SendAsync method. In the above example I simply put “Hello world” as the custom argument which can be retrieved as e.UserState.ToString().

The sending can be cancelled by the SmtpClient.SendAsyncCancel() method which can be triggered if e.g. the user clicks a button if it takes too long to send the email.

In case your SMTP server supports SSL then make sure you take advantage of it and send the email in an encrypted form:

client.EnableSsl = true;

Read all posts related to emailing in .NET here.

Externalising dependencies with Dependency Injection in .NET part 2: caching

Introduction

In the previous post we set up the startup project for our real discussion. In this post we’ll take a look at caching as a dependency.

Adding caching

Our glorious application is a success and grows by an incredible rate every month. It starts getting slow and the users keep complaining about the performance. The team decides to add caching to the service layer: cache the result of the “product by id” query to the repository for some time. The team decides to make this quick and modifies the GetProduct method as follows:

public GetProductResponse GetProduct(GetProductRequest getProductRequest)
{
	GetProductResponse response = new GetProductResponse();
	try
	{
		string storageKey = "GetProductById";
		ObjectCache cache = MemoryCache.Default;
		Product p = cache.Contains(storageKey) ? (Product)cache[storageKey] : null;
		if (p == null)
		{
			p = _productRepository.FindBy(getProductRequest.Id);
			CacheItemPolicy policy = new CacheItemPolicy() { AbsoluteExpiration = DateTime.Now.AddMinutes(5) };
			cache.Add(storageKey, p, policy);
		}
		response.Product = p;
		if (p != null)
		{
			response.Success = true;
		}
		else
		{
			response.Exception = "No such product.";
		}
	}
	catch (Exception ex)
	{
		response.Exception = ex.Message;
	}
	return response;
}

You’ll need to set a reference to the System.Runtime.Caching dll to find the ObjectCache object.

So we cache the product search result for 5 minutes. In order to see this in action add a second call to the the product service just above Console.ReadKey() in Main:

getProductResponse = productService.GetProduct(new GetProductRequest() { Id = 2 });

I encourage you to step through the code with F11. You’ll see that the first productService.GetProduct retrieves the product from the repository and adds the item to the cache. The second call fetches the product from the cache.

What’s wrong with this?

The code works, so where’s the problem?

Testability

The method is difficult to test in isolation because of the dependency on the ObjectCache class. The actual purpose of the method is to get a product by ID. However, if the code also caches the result then it’s difficult to test the result from GetProduct. If you want to get any reliable result from the test that tests the behaviour of this method you’ll need to somehow flush the cache before every test so that you know that got a fresh result, not a cached one. Otherwise if the test fails, then why did it fail? Was it a genuine failure, meaning that the product was not retrieved? Or was it because the caching mechanism failed? It’s the wrong approach making the test outcome dependent on such a dependency.

Flexibility

With this implementation we’re stuck with the ObjectContext as our caching solution. What if we want to change over to a different one, such as Memcached or HttpContext? In that case we’d need to go in and manually replace the ObjectCache solution to a new one. Even worse, let’s say all your service classes use ObjectCache for caching and you want to make the transition to another caching solution for all of them. You probably see how tedious, time consuming and error-prone this could be.

Single responsibility

The method also violates the Single Responsibility Principle as it performs caching in its method body. Strictly speaking it should not be doing this as it then introduces a hidden side effect. The caller of the method doesn’t know that the result is cached and may be surprised to see an outdated product if it’s e.g. updated directly in the database.

The solution will involve 2 patterns:

  • Adapter: to factor out the caching strategy behind an abstraction. We used caching to demonstrate the Adapter pattern and much of it will be re-used here.
  • Decorator: to completely break out the caching code from ProductService and put it in an encasing class

Step 1: hide caching

This is where we need to consider the operations expected from any caching engine. We should be able to store, retrieve and delete objects using any cache engine, right? Those operations should be common to all caching implementations.

As caching is a cross-cutting concern we’ll start building our infrastructure layer. Add a new C# class library called MyCompany.Infrastructure.Common to the solution and insert a new folder called Caching. Add the following interface to the Caching folder:

public interface ICacheStorage
{
	void Remove(string key);
	void Store(string key, object data);
	void Store(string key, object data, DateTime absoluteExpiration, TimeSpan slidingExpiration);
	T Retrieve<T>(string key);
}

Any decent caching engine should be able to fulfil this interface. Add the following implementation to the same folder. The implementation uses ObjectCache:

public class SystemRuntimeCacheStorage : ICacheStorage
{
	public void Remove(string key)
	{
		ObjectCache cache = MemoryCache.Default;
		cache.Remove(key);
	}

	public void Store(string key, object data)
	{
		ObjectCache cache = MemoryCache.Default;
		cache.Add(key, data, null);
	}

	public void Store(string key, object data, DateTime absoluteExpiration, TimeSpan slidingExpiration)
	{
		ObjectCache cache = MemoryCache.Default;
		var policy = new CacheItemPolicy
		{
			AbsoluteExpiration = absoluteExpiration,
			SlidingExpiration = slidingExpiration
		};

		if (cache.Contains(key))
		{
			cache.Remove(key);
		}

		cache.Add(key, data, policy);
	}
		
	public T Retrieve<T>(string key)
	{
		ObjectCache cache = MemoryCache.Default;
		return cache.Contains(key) ? (T) cache[key] : default(T);
	}
}

You’ll need to add a reference to the System.Runtime.Caching dll in the infrastructure layer as well. ObjectCache is an all-purpose cache which works with pretty much any .NET project type. However, say you’d like to go for an HttpContext cache then you can have the following implementation:

public class HttpContextCacheStorage : ICacheStorage
{
	public void Remove(string key)
	{
		HttpContext.Current.Cache.Remove(key);
	}

	public void Store(string key, object data)
	{
		HttpContext.Current.Cache.Insert(key, data);
	}

	public void Store(string key, object data, DateTime absoluteExpiration, TimeSpan slidingExpiration)
	{
		HttpContext.Current.Cache.Insert(key, data, null, absoluteExpiration, slidingExpiration);
	}

	public T Retrieve<T>(string key)
	{
		T itemStored = (T)HttpContext.Current.Cache.Get(key);
		if (itemStored == null)
			itemStored = default(T);

		return itemStored;
	}
}

We’ll need to be able to inject our caching implementation to the product service and not let the product service determine which strategy to take. In that case the implementation is still hidden to the caller and we still have a hard dependency on one of the concrete implementations. The revised ProductService looks as follows:

public class ProductService : IProductService
{
	private readonly IProductRepository _productRepository;
	private readonly ICacheStorage _cacheStorage;

	public ProductService(IProductRepository productRepository, ICacheStorage cacheStorage)
	{
		if (productRepository == null) throw new ArgumentNullException("ProductRepository");
		if (cacheStorage == null) throw new ArgumentNullException("CacheStorage");
		_productRepository = productRepository;
		_cacheStorage = cacheStorage;
	}

	public GetProductResponse GetProduct(GetProductRequest getProductRequest)
	{
		GetProductResponse response = new GetProductResponse();
		try
		{
			string storageKey = "GetProductById";
			Product p = _cacheStorage.Retrieve<Product>(storageKey);
			if (p == null)
			{
				p = _productRepository.FindBy(getProductRequest.Id);
				_cacheStorage.Store(storageKey, p, DateTime.Now.AddMinutes(5), TimeSpan.Zero);
					
			}
			response.Product = p;
			if (p != null)
			{
				response.Success = true;
			}
			else
			{
				response.Exception = "No such product.";
			}
		}
		catch (Exception ex)
		{
			response.Exception = ex.Message;
		}
		return response;
	}
}

You’ll need to add a reference to the infrastructure layer from the console.

We’ve successfully got rid of the ObjectCache dependency. You can even remove the System.Runtime.Caching dll from the references list in the Console layer. The ProductService creation code in Main is modified as follows:

IProductService productService = new ProductService(new ProductRepository(), new SystemRuntimeCacheStorage());

Run the code and you’ll see that it still works. Now ProductService is oblivious of the concrete implementation of ICacheStorage and you’re free to change it in the caller. You can then change the caching mechanism for all your caching needs by changing the concrete implementation of ICacheStorage.

Step 2: removing caching altogether

As it currently stands the GetProduct method still violates ‘S‘ in SOLID, i.e. the Single Responsibility Principle. The purpose of GetProduct is to retrieve a product from the injected repository, it has nothing to do with caching. The constructor signature at least indicates to the caller that there’s caching going on – the caller must send an ICacheStorage implementation – but testing the true purpose of GetProduct is still not straightforward.

Luckily we have the Decorator pattern hinted at above to solve the problem. I’ll not go into the details of the pattern here, you can read about it in great detail under the link provided. In short it helps to augment the functionality of a class in an object-oriented manner by building an encasing, “enriched” version of the class. That’s exactly what we’d like to build: augment the plain ProductService class with caching. Let’s see what this could look like.

The Decorator pattern has a couple of different implementations, here’s one variant. Add the following implementation of IProductService into the Services folder:

public class CachedProductService : IProductService
{
	private readonly IProductService _productService;
	private readonly ICacheStorage _cacheStorage;

	public CachedProductService(IProductService productService, ICacheStorage cacheStorage)
	{
		if (productService == null) throw new ArgumentNullException("ProductService");
		if (cacheStorage == null) throw new ArgumentNullException("CacheStorage");
		_cacheStorage = cacheStorage;
		_productService = productService;
	}

	public GetProductResponse GetProduct(GetProductRequest getProductRequest)
	{
		GetProductResponse response = new GetProductResponse();
		try
		{
			string storageKey = "GetProductById";
			Product p = _cacheStorage.Retrieve<Product>(storageKey);
			if (p == null)
			{
				response = _productService.GetProduct(getProductRequest);
				_cacheStorage.Store(storageKey, response.Product, DateTime.Now.AddMinutes(5), TimeSpan.Zero);
			}
                        else
			{
				response.Success = true;
				response.Product = p;
			}
		}
		catch (Exception ex)
		{
			response.Exception = ex.Message;
		}
		return response;
	}
}

We delegate both the product retrieval and the caching to the injected implementations of the abstractions.

ProductService.cs can be changed back to its original form:

public class ProductService : IProductService
{
	private readonly IProductRepository _productRepository;

	public ProductService(IProductRepository productRepository)
	{
		if (productRepository == null) throw new ArgumentNullException("ProductRepository");
		_productRepository = productRepository;
	}

	public GetProductResponse GetProduct(GetProductRequest getProductRequest)
	{
		GetProductResponse response = new GetProductResponse();
		try
		{
			Product p = _productRepository.FindBy(getProductRequest.Id);
			response.Product = p;
			if (p != null)
			{
				response.Success = true;
			}
			else
			{
				response.Exception = "No such product.";
			}
		}
		catch (Exception ex)
		{
			response.Exception = ex.Message;
		}
		return response;
	}
}

The calling code in Main will look as follows:

static void Main(string[] args)
{
	IProductService productService = new ProductService(new ProductRepository());
	IProductService cachedProductService = new CachedProductService(productService, new SystemRuntimeCacheStorage());
	GetProductResponse getProductResponse = cachedProductService.GetProduct(new GetProductRequest() { Id = 2 });
	if (getProductResponse.Success)
	{
		Console.WriteLine(string.Concat("Product name: ", getProductResponse.Product.Name));
	}
	else
	{
		Console.WriteLine(getProductResponse.Exception);
	}

	getProductResponse = cachedProductService.GetProduct(new GetProductRequest() { Id = 2 });

	Console.ReadKey();
}

Note how we first create a ProductService which is then injected into the CachedProductService along with the selected caching strategy.

So now ProductService can be tested in isolation.

Plan B: Null object caching

You are not always in control of all parts of the source code. In other cases changing ProductService like that may cause a long delay in development time due to tightly coupled code. So imagine that you have to use a product service like we had after step 1:

public ProductService(IProductRepository productRepository, ICacheStorage cacheStorage)

So you have to inject a caching strategy but still want to test the true purpose of GetMessage, i.e. bypass caching altogether. The Null Object pattern comes to the rescue. So we create a dummy implementation of ICacheStorage that doesn’t do anything. Add the following implementation into the Caching folder of the infrastructure layer:

public class NoCacheStorage : ICacheStorage
{
	public void Remove(string key)
	{}

	public void Store(string key, object data)
	{}

	public void Store(string key, object data, DateTime absoluteExpiration, TimeSpan slidingExpiration)
	{}

	public T Retrieve<T>(string key)
	{
		return default(T);
	}
}

You can inject this dummy implementation to ProductService to eliminate all caching:

IProductService productService = new ProductService(new ProductRepository(), new NoCacheStorage());

In the next post we’ll look at how to hide reading from a configuration file.

View the list of posts on Architecture and Patterns here.

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

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