C# Repository Design Pattern for Database Operations in a .NET Core 3.1 MVC Web App

C# Repository Design Pattern for Database Operations in a .NET Core 3.1 MVC Web App

Introduction

When building applications, it is important to consider how and where you’re conducting database operations.

Entity Framework Database Context (DbContext) and the Controller

Building a basic template for a .NET Core 3.1 application using a scaffolding approach like the one from this Microsoft tutorial is a great starting point. Firstly, let’s have a look at a small code snippet generated from the scaffolding.

In this example, the PusheenController class has actions for CRUD (Create, Read, Update and Delete) operations against the database. Here, we are directly interacting with the Entity Framework DbContext class called PusheenCustomExportCsvContext and retrieving data about Pusheens from the database. The PusheenCustomExportCsvContext is injected as a dependency into the PusheenController. In this web app, dependencies are added to the service container in the ConfigureServices method in Startup.cs.

However, it is easy to end up with big controllers; big in the sense that there’s a lot of database operations logic built into the controller. Since the DbContext is a dependency of the controller, a further issue faced is if you were to test this, you would have to mock the DbSet and DbContext. It is definitely achieveable to mock we like; but we would have to mock the Provider, Expression, ElementType properties and GetEnumerator() method.

In larger applications, we would like to separate the concerns out into layers that are responsible for the business logic, presentation, database etc.

Example 1: DbContext and PusheenController

//Code omitted for brevity 🙂
namespace PusheenCustomExportCsv.Web.Controllers
{
    public class PusheenController : Controller
    {
        private readonly PusheenCustomExportCsvContext _context;

        public PusheenController(PusheenCustomExportCsvContext context)
        {
            _context = context;
        }

        //Code omitted for brevity 🙂

        // GET: Pusheen/Details/5
        public async Task<IActionResult> Details(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var pusheen = await _context.Pusheens
                .SingleOrDefaultAsync(m => m.ID == id);
            if (pusheen == null)
            {
                return NotFound();
            }

            return View(pusheen);
        }

        // GET: Pusheen/Create
        public IActionResult Create()
        {
            return View();
        }

        // POST: Pusheen/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create([Bind("Id,Name,FavouriteFood,SuperPower")] Pusheen pusheen)
        {
            if (ModelState.IsValid)
            {
                _context.Add(pusheen);
                await _context.SaveChangesAsync();
                return RedirectToAction("Index");
            }
            return View(pusheen);
        }

What is the goal of the Repository Design Pattern and why is it useful?

Let’s assume we would like a presentation layer made up of controllers and views, and a service layer for the business logic and database operations. We can create a repository (in this case lumped into the service for simplicity) where our database operations and logic can sit.

The repository is in charge of interacting with the Entity Framework DbContext class, so the controller doesn’t have to.

Repository: Defining and Implementing the Interface

In this interface, we define an IPusheenService and implement the interface in the PusheenService.

Example 2: IPusheenService

//Code omitted for brevity 🙂
namespace PusheenCustomExportCsv.Web.Services
{
    public interface IPusheenService
    {
        Task<List<Pusheen>> GetAllAsync();
        Task<Pusheen> Create(Pusheen pusheen);
        Task<Pusheen> Update(Pusheen pusheen);
        Task<Pusheen> Delete(Pusheen pusheen);
        Task<Pusheen> FindPusheenAsync(int? id);
        Task<Pusheen> FindPusheenById(int? id);
        bool PusheenExists(int id);

    }
}

Below is an example of how PusheenService implements FindPusheenAsync and FindPusheenById. These database operations were originally coded directly into the controller as we saw in Example 1.

Example 3: PusheenService

//Code omitted for brevity 🙂

        public async Task<Pusheen> FindPusheenAsync(int? id)
        {
            var pusheen = await _context.Pusheens.FindAsync(id);
            return pusheen;
        }

//Code omitted for brevity 🙂

        public async Task<Pusheen> FindPusheenById(int? id)
        {
            var pusheen = await _context.Pusheens
                .FirstOrDefaultAsync(m => m.Id == id);
            return pusheen;
        }

//Code omitted for brevity 🙂

Let’s see what our controller looks like now. The key difference is that the PusheenController is a lot slimmer and we don’t need to interact with the DbContext directly anymore; that’s the job of the repository now! 🙂

Example 4: PusheenController

//Code omitted for brevity 🙂
namespace PusheenCustomExportCsv.Web.Controllers
{
    public class PusheenController : Controller
    {
        private readonly IPusheenService _pusheenService;

        public PusheenController(IPusheenService pusheenService)
        {
            _pusheenService = pusheenService;
        }

//Code omitted for brevity 🙂

        // GET: Pusheen/Details/5
        public async Task<IActionResult> Details(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var pusheen = await _pusheenService.FindPusheenById(id);

            if (pusheen == null)
            {
                return NotFound();
            }

            return View(pusheen);
        }

        // GET: Pusheen/Create
        public IActionResult Create()
        {
            return View();
        }

        // POST: Pusheen/Create
        // To protect from overposting attacks, enable the specific properties you want to bind to, for 
        // more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create([Bind("Id,Name,FavouriteFood,SuperPower")] Pusheen pusheen)
        {
            if (ModelState.IsValid)
            {
                await _pusheenService.Create(pusheen);
                return RedirectToAction(nameof(Index));
            }
            return View(pusheen);
        }
//Code omitted for brevity 🙂

Final Thoughts

I hope you find this post useful and being part of my coding journey! Thank you for reading my blog! 🙂

You can find the link to my Github repo with the simple web app example here.

C# Unit Testing a Custom FileResult That Exports Data into a CSV file Using Streaming in a .NET Core 3.1 MVC App

C# Unit Testing a Custom FileResult That Exports Data into a CSV file Using Streaming in a .NET Core 3.1 MVC App

Introduction

In the past two months at work, I was tasked with learning C#, as well as creating a web app using the .NET Core 3.1 MVC framework. I wanted to document the most interesting concepts in a series of blog posts.

In my last blog, I demonstrated how to create a custom FileResult to export data into a CSV file using streaming in a .NET Core 3.1 MVC web app. In this follow on blog post, I will show you how to unit test the custom FileResult and the controller which produces this FileResult.

Again, the actual code was more complex; this blog was my attempt to abstract the core concepts into a simple web app using Pusheen the Cat as a fun example!

Unit Testing Custom FileResult With Streaming in .NET Core 3.1

In my last blog, I had a custom FileResult called PusheenCsvResult. To set the scene for unit testing, I used the NUnit testing framework, along with the FluentAssertions and FluentAssertions.AspNetCore.Mvc libraries, which provided a clear way to communicate what I was asserting in my test (i.e. what was the expected result). I applied the Arrange, Act, Assert structure for this and am still learning the best way to do it!

P.S. Would highly recommend the book Agile Technical Practices Distilled: A learning journey in technical practices and principles of software design.

What was the goal?

Let’s start off with the goal! When we’re working with unit testing, it’s helpful to define what it is we’re checking for. In this unit testing situation, I wanted a way to check that the PusheenCsvResult’s method ExecuteResultAsync was streaming (writing) the response correctly to the HttpContext’s response body.

How did I go about doing it?

Knowing this, I followed some tips in the Agile Technical Practices Distilled book to start from the assertion and work backwards (Assert, Act , Arrange). I didn’t just magically know what I needed; it took some time to get there.

Setting it up

I created a [TestFixture] for testing PusheenCsvResult and within the [SetUp], I defined a _httpContext_fileDownloadName and made a fake _fakeActionContext object.

The reason I did this was because the PusheenCsvResult’s method ExecuteResultAsync, took a parameter which was of type ActionContext.

Let’s recap on my last blog for a second, the job of ExecuteResultAsync is using StreamWriter to write to the response body of the HttpContext of the ActionContext and the stream sits in between the application and in this case the response body. The data is written to the stream (response body) and then from the stream, it then results in the CSV file being produced.

In the case of the unit test scope, I wanted to create a _fakeActionContext object as an instance of ActionContext and in it’s constructor, I set the HttpContext as the _httpContext I defined earlier in my test [SetUp]. This enabled the ability to check what was written to the response body of that _httpContext.

#Code omitted for brevity

    [TestFixture]
    public class PusheenCsvResultShould
    {
        private PusheenCsvResult _pusheenCsvResult;
        private string _fileDownloadName;
        private string _expectedResponseText;
        private DefaultHttpContext _httpContext;
        private ActionContext _fakeActionContext;

        [SetUp]
        public void Setup()
        {
            _httpContext = new DefaultHttpContext();

            _fileDownloadName = "pusheen.csv";

            _fakeActionContext = new ActionContext()
            {
                HttpContext = _httpContext
            };
        }
        
        [Test]
        public async Task GivenActionContext_ExecuteResultAsync_ShouldWriteLineToHttpResponseBody()
        {
            
            //Arrange
            var data = new List<Pusheen>()
            {
                new Pusheen() { Id = 1, Name = "Pusheen", FavouriteFood = "Ice cream", SuperPower = "Baking delicious cookies" },
                new Pusheen() { Id = 2, Name = "Pusheenosaurus", FavouriteFood = "Leaves", SuperPower = "Roarrrrr!" },
                new Pusheen() { Id = 3, Name = "Pusheenicorn", FavouriteFood = "Butterfly muffins", SuperPower = "Making rainbow poop" }
                
            }.AsQueryable();

            PusheenCsvResult _pusheenCsvResult = new PusheenCsvResult(data, _fileDownloadName);

            _expectedResponseText = System.IO.File.ReadAllText(TestContext.CurrentContext.TestDirectory + @"/TestData/expectedCsv.txt");

            var memoryStream = new MemoryStream();
            _httpContext.Response.Body = memoryStream;

            //Act
            await _pusheenCsvResult.ExecuteResultAsync(_fakeActionContext);
            var streamText = System.Text.Encoding.Default.GetString(memoryStream.ToArray());

            //Assert
            streamText.Should().Be(_expectedResponseText);
        }

    }

Let’s hop over to the test

For the [Test] itself, I checked that given an ActionContext, the method ExecuteResultAsync should WriteLine to the HttpContext response body.

I needed a PusheenCsvResult for my test, and that took 2 parameters for it’s constructor.

  1. data (as type IQueryable<Pusheen>)
  2. fileDownloadName (as type String)

I already defined fileDownloadName earlier in the [SetUp], so the next step was to make some data for the test scenario. In this case, a new List<Pusheen> as .AsQueryable() was created and I passed this into PusheenCsvResult’s constructor.

Based on this information, I wanted to make a file containing the expected text I would expect to see as _expectedResponseText. In my assertion, I checked that the text I got from the stream should match the _expectedResponseText for the test to pass.

Now, this was the tricky bit – how to deal with closed streams?

When I was testing this, I didn’t know what was wrong, as the test kept saying that it couldn’t access a closed stream. Since I defined the StreamWriter within a using block; the stream will be closed once it’s done its job. This was not a bad thing and is something I recommend doing in your implementation; but it meant I needed another way to access what was written to the stream for the purpose of the unit testing (in this case, the stream was the HttpContext’s response body itself).

I added some comments on the code snippet below to describe what was going on.

 // I create a new Memory Stream and set that stream as the Response Body of the _httpContext I'm using in my unit test scope
var memoryStream = new MemoryStream();
_httpContext.Response.Body = memoryStream;

//Act
// I await and pass the _fakeActionContext to my ExecuteResultAsync method. Reminder that I pointed the HttpContext of ActionContext to the _httpContext I made for testing
await _pusheenCsvResult.ExecuteResultAsync(_fakeActionContext);

//I need to make sure that I capture the contents of the memoryStream and store it against the variable streamText which I can access later in my assertion
var streamText = System.Text.Encoding.Default.GetString(memoryStream.ToArray());

Unit testing the Controller

The controller was a bit more straightforward. I used Moq to mock the PusheenService and its method GetAllPusheens() to return some data.

_mockPusheenService.Setup(p => p.GetAllPusheens()).Returns(data);

Here, I tested that ExportCsv on the PusheenController returned the result of type PusheenCsvResult and that the fileDownloadName and contentType were correct.

#The rest of the code has been omitted for brevity! 🙂

namespace PusheenCustomExportCsv.Tests.Controllers
{
    [TestFixture]
    public class PusheenControllerShould
    {
        private PusheenController _controller;
        private Mock<IPusheenService> _mockPusheenService;
        private Mock<IConfiguration> _mockConfig;
        private DbContextOptions<PusheenCustomExportCsvContext> _testDbOptions;
        private PusheenCustomExportCsvContext _testDbContext;
        
        [SetUp]
        public void Setup()
        {
            _mockPusheenService = new Mock<IPusheenService>();
            _controller = new PusheenController(_mockPusheenService.Object);
        }
        
        [Test]
        public void ExportCsv_Returns_CsvResult()
        {
            //Arrange
            var data = new List<Pusheen>()
            {
                new Pusheen() { Id = 1, Name = "Pusheen", FavouriteFood = "Ice cream", SuperPower = "Baking delicious cookies" },
                new Pusheen() { Id = 2, Name = "Pusheenosaurus", FavouriteFood = "Leaves", SuperPower = "Roarrrrr!" },
                new Pusheen() { Id = 3, Name = "Pusheenicorn", FavouriteFood = "Butterfly muffins", SuperPower = "Making rainbow poop" }
                
            }.AsQueryable();

            _mockPusheenService.Setup(p => p.GetAllPusheens()).Returns(data);

            //Act
            var result = _controller.ExportCsv();

            //Assert
            result.Should().BeOfType(typeof(PusheenCsvResult));
            result.FileDownloadName.Should().Be("pusheen.csv");
            result.ContentType.Should().Be("text/csv");

        }

        
    }
}

Final Thoughts

I hope you find this post useful and being part of my coding journey! Thank you for reading my blog! 🙂

You can find the link to my Github repo with the simple web app example containing the custom fileresult and test project here.

C# Creating a Custom FileResult to Export Data into a CSV file Using Streaming in a .NET Core 3.1 MVC App

C# Creating a Custom FileResult to Export Data into a CSV file Using Streaming in a .NET Core 3.1 MVC App

Introduction: What is the goal?

In the past two months at work, I was tasked with learning C#, as well as creating a web app using the .NET Core 3.1 MVC framework. I wanted to document the most interesting concepts in a series of blog posts.

In this blog, I will show you how to create a custom fileresult to export data into a CSV file using streaming in a .NET Core 3.1 MVC web app. This came about as I was asked to give the user the ability to export their dataset into a CSV file through the web app, but there was a lot of data to deal with. The goal here was to create something to export to a CSV file taking performance into account.

The actual code was more complex; this blog is my attempt to abstract the core concepts into a simple web app using Pusheen the Cat as a fun example!

P.S. If you don’t know who Pusheen is, I am utterly obsessed with it. You’ll probably recognise Pusheen from numerous gifs and stickers on social media.

C# Streaming: What is it and why is it useful?

Imagine you had a lot of data to read/write as part of a web app solution; you probably want to break it down into bitesize pieces, as you won’t want to read/write a large file in one go! This can be achieved through streaming.

A stream sits between the application and the file; the benefit of using streaming is the read/write operations are a lot smoother. When you’re writing data to somewhere, it is written to the stream and then from the stream, it then goes to your chosen destination, usually a file. When you’re reading data, you read to the stream and then your web app can then read from the stream.

If you like to read on a bit more, this article by Guru99 is particularly helpful.

“C# provides standard IO (Input/Output) classes to read/write from different sources like a file, memory, network, isolated storage, etc. System.IO.Stream is an abstract class that provides standard methods to transfer bytes (read, write, etc.) to the source. It is like a wrapper class to transfer bytes. Classes that need to read/write bytes from a particular source must implement the Stream class.” – Extracted from TutorialsTeacher

Creating a Custom FileResult With Streaming in .NET Core 3.1

Why Create a Custom Fileresult in the First Place?

The .NET Core 3.1 MVC framework provides the ActionResult class. Here’s the link to the docs for it. It implements the IActionResult interface. ActionResult is essentially the return type of a controller method, it is the base class for lots of result classes to return models to views, file streams and more! There are many derived classes to choose from, but there wasn’t one to produce a CSV result. I wanted a FileResult which has streaming and exported to CSV.

The cool thing was one of the derived classes is FileResult. This class represents an ActionResult that when executed will write a file as the response. We can extend on this class to create a custom FileResult to create a CSV FileResult.

Example of Custom FileResult With Streaming in .NET Core 3.1

Here is an example of a PusheenCsvResult which extends FileResult. As it is specific to exporting data about Pusheen the Cat, we give it some pusheenData as IEnumerable<Pusheen>. In it’s constructor, we pass in that data along with the fileDownloadName and set the file type to be "text/csv".

As we’re extending on FileResult, we override ExecuteResultAsync with our implementation. In this example, we’re using StreamWriter to write to the response body of the HttpContext of the ActionContext. We write the header row for our CSV file and then iterate through our _pusheenData and write that data. As a reminder, the stream sits in between the application and in this case the response body; the data is written to the stream (response body) and then from the stream, it then results in the CSV file being produced.

We define the StreamWriter within a using block. We use StreamWriter.FlushAsync method to clear all buffers for the current writer and results in any buffered data to be written to the underlying stream.

using System;
using System.IO;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;


namespace PusheenCustomExportCsv.Web.Models
{
    public class PusheenCsvResult : FileResult
    {
        private readonly IEnumerable<Pusheen> _pusheenData;

        public PusheenCsvResult(IEnumerable<Pusheen> pusheenData, string fileDownloadName) : base("text/csv")
        {
            _pusheenData = pusheenData;
            FileDownloadName = fileDownloadName;
        }

        public async override Task ExecuteResultAsync(ActionContext context)
        {
            var response = context.HttpContext.Response;
            context.HttpContext.Response.Headers.Add("Content-Disposition", new[] { "attachment; filename=" + FileDownloadName });

            using (var streamWriter = new StreamWriter(response.Body)) {
              await streamWriter.WriteLineAsync(
                $"Pusheen, Food, SuperPower"
              );
              foreach (var p in _pusheenData)
              {
                await streamWriter.WriteLineAsync(
                  $"{p.Name}, {p.FavouriteFood}, {p.SuperPower}"
                );
                await streamWriter.FlushAsync();
              }
              await streamWriter.FlushAsync();
            }
        }

    }
}

Using the Custom FileResult in the Controller

Now that we have the PusheenCsvResult class, we can go ahead and use it in the controller.

#The rest of the code has been omitted for brevity! 🙂

public FileResult ExportCsv()
{
    return File(_pusheenService.GetAllPusheens(), "pusheen.csv");
}

public virtual PusheenCsvResult File(IEnumerable<Pusheen> pusheenData, string fileDownloadName)
{
    return new PusheenCsvResult(pusheenData, fileDownloadName);
}

App Demo

Here’s some screenshots of what it looks like from the front-end!

1.jpg
2.png
3.png
4.png

Final Thoughts

I hope you found this useful! I thought an improvement for this PusheenCsvResult class would be to make it generic, so that it can work with all kinds of datasets, not just the Pusheen dataset! That’s for another day! 🙂

Watch out for my next blog on unit testing the Custom FileResult which Exports Data into a CSV file Using Streaming in a .NET Core 3.1 MVC App. I have a series of .NET Core MVC blogs coming up, so it should be exciting times!

Thank you for reading my blog! 🙂

You can find the link to my Github repo with the simple web app example containing the custom fileresult here.