This is a tutorial on how to implement a simple C# console application for caching the filenames (i.e., absolute paths) in a given directory using IMemoryCache and how to use it via Dependency Injection. This can be used, for example, for maintaining a cache of all image files in a given directory. For this tutorial, I am using .NET 6.0 and Visual Studio Community 2022 .
Generally, this is what we are going to follow to accomplish this task:
- Implement DirectoryCacher class
- Use DirectoryCacher in Program.cs
Download project here.
Create Console App project
Open Visual Studio, create a Console Application and call it DirectoryCacherConsoleApp. For the Solution name, enter ProjectCache.
I won’t talk about the project creation in depth, but I do recommend reading my previous post called Start a New Project in Visual Studio 2022 and .NET 6.0 which describes how to create two projects within a solution. In that tutorial, I created an ASP.NET Core Web API application, and a Console Application.
After you have created the project, your Solution Explorer should look like this:
Create a directory or directories with files
For the sake of simplicity, let’s put our directory in the bin directory because our executable resides in it and is considered our current directory. Create one or two folders with files in them. Specifically, put them in \bin\Debug\net6.0 directory as illustrated below:
If you click the Show All Files icon on top, it will show you the bin directory and other hidden folders. Here, under net6.0 folder is Images folder, and in it I placed two folders – abstract and biology. Each of these two folders contains jpeg files.
If you have downloaded the project I provided above, then you should find an Images folder under \bin\Debug\net6.0 directory. That folder in turn contains two folders under it, and each folder contains at least three files.
Implement DirectoryCacher class
DirectoryCacher has one main method called GetListCache()
which returns a string that tells you whether the list of files were from the drive or the cache.
Pseudocode
To understand what GetListCache()
does, look at the pseudocode below:
DirectoryCacher(IMemoryCache)
{
}
string GetListCache()
{
Get content of cache, if any, using directory path as key
if (content of cache is empty)
{
Get all file paths inside the directory
Put all file paths inside a list
Cache the list
Return the message "Reading directory from drive"
}
else
{
Return the message "Reading from cache"
}
}
I request an instance of IMemoryCache in the constructor on line 1. On line 7 I read the cache using the directory path as the key, and on Line 9 I check if cache is empty and, if it is, I cache the list of file paths. I, then, return a message that says we read the list from the drive. Otherwise, when the cache is not empty, I just return a message saying it read the list from cache.
Source Code
Check out the complete source code below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
namespace DirectoryCacherConsoleApp;
public class DirectoryCacher
{
IMemoryCache m_memoryCache;
public DirectoryCacher(IMemoryCache memoryCache)
{
m_memoryCache = memoryCache;
}
public string GetListCache(string path)
{
string msg = string.Empty;
var dirInfo = new DirectoryInfo(path);
if (dirInfo.Exists)
{
List<string> cacheList = new List<string>();
m_memoryCache.TryGetValue(path, out cacheList);
if (cacheList == null)
{
BuildCache(path);
m_memoryCache.TryGetValue(path, out cacheList);
msg = "Reading directory from drive.";
}
else
{
msg = "Reading directory from cache.";
}
}
else
{
throw new DirectoryNotFoundException();
}
return msg;
}
private void BuildCache(string path)
{
// Get a list of files from the given path
var extensions = new string[] { ".png", ".jpg", ".gif" };
var dirInfo = new DirectoryInfo(path);
List<System.IO.FileInfo> fileInfoList = dirInfo.GetFiles("*.*").Where(f => extensions.Contains(f.Extension.ToLower())).ToList();
//
// Check if directory is empty or not
//
if (fileInfoList.Count() != 0)
{
// Put all file names in our list
List<string> fileInfo2string = fileInfoList.Select(f => f.FullName.ToString()).ToList();
// Set cache options.
var memCacheEntryOptions = new MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromSeconds(120));
// Cache the list
m_memoryCache.Set(path, fileInfo2string, memCacheEntryOptions);
}
else
{
throw new FileNotFoundException();
};
}
}
Brief explanation of code
As I mentioned earlier, I get IMemoryCache instance via dependency injection in the constructor, on line 13. On line 26, I read the value of the cache using the directory path as the key. On line 28, if cache is empty, then I call BuildCache()
function, which reads the contents of the directory, puts the contents of the files in a list, caches the list, and finally returns “Reading directory from drive”. Otherwise, it just returns “Reading directory from cache”.
Also, notice, on line 23, I check the existence of the directory path, and if it doesn’t exist, I an exception at line 41. On line 64 I set cache options to specify the expiration of the cache to 120 seconds (you may change it to whatever you want). And finally, on line 67, I cache the list using directory path as the key to cache.
Using DirectoryCacher in Program.cs
The main program, Program.cs, contains one line of code initially:
Console.WriteLine("Hello, World!");
To be able to get our DirectoryCacher application working, do the following:
- Install Microsoft.Extensions.Caching.Memory
- Install Microsoft.Extensions.Hosting
- Get an instance of IMemoryCache via IHost
- Call DirectoryCacher multiple times with different image files.
To install Microsoft.Extensions.Caching.Memory, right click on the console project, then click Manage Nuget Packages.
As illustrated above, enter memory in the search bar. Make sure you click Browse above it. Click on Microsoft.Extensions.Caching.Memory. On the right panel, click Install.
Do the same thing to install Microsoft.Extensions.Hosting. Enter hosting in the search bar to find it.
As I mentioned, I need to get an instance of IMemoryCache via Hosting extension. Below is the complete source code for our Program.cs.
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services => services.AddMemoryCache())
.Build();
IMemoryCache cache = host.Services.GetRequiredService<IMemoryCache>();
string dir1 = Environment.CurrentDirectory + "\\Images\\abstract\\";
string dir2 = Environment.CurrentDirectory + "\\Images\\biology\\";
DirectoryCacherConsoleApp.DirectoryCacher dirCacher = new DirectoryCacherConsoleApp.DirectoryCacher(cache);
try
{
// First Pass
Console.WriteLine(dirCacher.GetListCache(dir1));
Console.WriteLine(dirCacher.GetListCache(dir2));
// Second Pass
Console.WriteLine(dirCacher.GetListCache(dir1));
Console.WriteLine(dirCacher.GetListCache(dir2));
}
catch (Exception ex)
{
// Do something
}
Brief explanation of the main program
Line 5 and line 9 shows how to get an instance of IMemoryCache through Dependency Injection (FYI: this is done differently in a web-based application as you will see in my later posts). On line 11 and 12, I simply hard coded the directories for demonstration purposes. On line 14, I create an instance of our DirectoryCacher.
First pass
Line 20 to 21 is the first pass where I call GetListCache() for the first time for each directory. And because this is the first time the application is reading the directory from the local drive, I expect a return message that says “Reading directory from drive” from each call.
Second pass
Line 24 to 25 is the second pass, and because we had just previously accessed the directories in the first pass, we expect a return message that says “Reading directory from cache” from each call.
Running the program
Hit F5 to run your program. You should get a console window that looks like:
Conclusion
This is a simple directory content caching tutorial using .NET 6.0 and Visual Studio Community 2022 . The application demonstrates the use of IMemoryCache service for caching in a console application. This tutorial demonstrates how to use the Hosting extension to be able to add MemoryCache in the services collection, and get it ready for use. This also demonstrates how to use Dependency Injection in the application constructor to be able to get an instance of IMemoryCache.