Introduction
If you’re a software professional, then you’re familiar with the Software enhancement and maintenance work. This is the part of the software development life cycle, so you can correct the faults, and delete or enhance existing features. The software maintenance cost can be minimized if you use a software architectural pattern, choose the right technologies, and are aware of the industry trends for the future. It also helps to consider resource reliability/availability for now and the future, use a design pattern/principle in your code, re-use your code and keep your options open for future extensions, etc. Anyway, if you use any known software architectural pattern in your application, then it will be easy for others to understand the structure/component design of your application. I’ll explain a sample project implementation according to the CQRS pattern using MediatR in ASP.NET Core MVC with vue.js.
Coverage Topics
- Implementation of CQRS Pattern
- Configure MediatR Package for ASP.NET Core
- Data read/write Handler Implantation using MediatR
- Integrating MediatR with API Controller/Controller
- Passing Command/Query Object using MediatR
- Converting image to byte array/byte array to base64 string
Prerequisites
- Visual Studio 2019 for the .NET Core 3.1
- Install Node.js, NPM and @Vue/CLI
- Need WebPack & NPM Task Runner
- Recommend to read “Integrating/Configuring Vue.js in ASP.NET Core 3.1 MVC”
Drilldown the Basic Shorty
- CQRS Pattern: In a short, Command – Query Separation Responsibility (CQRS) pattern separates the READ query operation which returns the data without changing the database/system and WRITE command (insert/update/delete) operation which changes the data into the database/system. NEVER mix the read and write operation together.
- Mediator Pattern: This is a design pattern that has an impact on the code where Mediator is used when you need centralized control and communication between multiple classes/objects. For example, Facebook Messenger is a mediator to send messages to multiple users.
- MVC Pattern: This is an architectural pattern for the application where Model, View, and Controller are separated by their responsibility. Model is the data of an object; View presents data to the user and handles user interaction, and the Controller acts as a mediator between View & Model.
Application Solution Structure
The main goal of this project is to explain the CQRS architectural pattern. I’m planning to implement a tiny Single-Page-Application (SPA) project. The choice of technology is important, and you should choose it according to your requirements. For the User Interface (UI) and Presentation Logic Layer (PLL), I chose ASP.NET Core MVC and Vue.js (JavaScript framework). For the data access, I chose the Entity Framework (EF) Core Code First Approach. It will be implemented into the Data-Access-Layer (DAL). Intentionally, I’m avoiding a separate Business Logic Layer (BLL) and other layers to minimize the length of this article.
Image Upload & Display Application
In this project, considering the CQRS pattern, at first, I will upload the image file to save it into the database; it will explain the write command operation. Secondly, I will read the data from the database to display the image; it will explain the read query operation.
I’ve added two separate projects in the same solution. One is the ClassLibrary (.NET Core) project which is named “HR.App.DAL.CQRS” and another is the ASP.NET Core Web Application project which is named “HR.App.Web”.

Communication Design Between MVC & JS Framework
In this stage, I’m pointing the UI/PLL and how they will talk to each other. Look at the diagram below, I’m placing the JS framework in between the View and Web API Controller.

According to the above diagram, ASP.NET MVC Controller renders the View. JS
passes the HTTP request (GET/PUT/POST/DELETE) from the view to the Web API
Controller as well as update the response data (JSON/XML) from Web API
Controller to the View.

Note
I’m guessing, you know, how to configure the Vue.js in ASP.NET Core MVC project. If you need the step by step instructions to configure the Vue.js in the ASP.NET Core with a sample project, then recommended reading: "Integrating/Configuring Vue.js in ASP.NET Core 3.1 MVC"
I’m guessing, you know, how to configure the Vue.js in ASP.NET Core MVC project. If you need the step by step instructions to configure the Vue.js in the ASP.NET Core with a sample project, then recommended reading: "Integrating/Configuring Vue.js in ASP.NET Core 3.1 MVC"
In SPA, Adding the UI and PLL in the Presentation Layer
In “HR.App.Web” project, add the Index.cshtml view and Index.cshtml.js file. I add the following HTML scripts for the Image upload and Image view tag/control into the Index.cshtml. These are associated with reading and write actions.
- @{
- ViewData["Title"] = "Home Page";
- }
- <div id="view" v-cloak>
- <div class="card">
- <div class="card-header">
- <div class="row">
- <div class="col-10">
- <h5>Upload File</h5>
- </div>
- </div>
- </div>
- <div class="card-body">
- <dropzone id="uploadDropZone" url="/HomeApi/SubmitFile"
- :use-custom-dropzone-options="useUploadOptions"
- :dropzone-options="uploadOptions" v-on:vdropzone-success="onUploaded"
- v-on:vdropzone-error="onUploadError">
- <!-- Optional parameters if any! -->
- <input type="hidden" name="token" value="xxx">
- </dropzone>
- </div>
- </div>
- <br/>
- <div class="card">
- <div class="card-header">
- <div class="row">
- <div class="col-10">
- <h5>Image viewer</h5>
- </div>
- </div>
- </div>
- <div class="card-body">
- <img v-bind:src="imageData" v-bind:alt="imageAlt" style="width:25%;height:25%; display: block;margin-left: auto; margin-right: auto;" />
- <hr />
- <div class="col-6">
- <button id="viewFile" ref="viewFileRef" type="button" class="btn btn-info" v-on:click="viewImageById">View Image</button>
- <button type="button" class="btn btn-info" v-on:click="onClear">Clear</button>
- </div>
- </div>
- </div>
- </div>
- <script type="text/javascript">
- </script>
- <script type="text/javascript" src="~/dest/js/home.bundle.js" asp-append-version="true"></script>
Add the following Vue.js script for the HTTP GET and POST request into the Index.cshtml.js file:
- import Vue from 'vue';
- import Dropzone from 'vue2-dropzone';
- document.addEventListener('DOMContentLoaded', function (event) {
- let view = new Vue({
- el: document.getElementById('view'),
- components: {
- "dropzone": Dropzone
- },
- data: {
- message: 'This is the index page',
- useUploadOptions: true,
- imageData: '',
- imageAlt: 'Image',
- imageId: 0,
- uploadOptions: {
- acceptedFiles: "image/*",
- //acceptedFiles: '.png,.jpg',
- dictDefaultMessage: 'To upload the image click here. Or, drop an image here.',
- maxFiles: 1,
- maxFileSizeInMB: 20,
- addRemoveLinks: true
- }
- },
- methods: {
- onClear() {
- this.imageData = '';
- },
- viewImageById() {
- try {
- this.dialogErrorMsg = "";
- //this.imageId = 1;
- var url = '/HomeApi/GetImageById/' + this.imageId;
- console.log("===URL===>" + url);
- var self = this;
- axios.get(url)
- .then(response => {
- let responseData = response.data;
- if (responseData.status === "Error") {
- console.log(responseData.message);
- }
- else {
- self.imageData = responseData.imgData;
- console.log("Image is successfully loaded.");
- }
- })
- .catch(function (error) {
- console.log(error);
- });
- } catch (ex) {
- console.log(ex);
- }
- },
- onUploaded: function (file, response) {
- if (response.status === "OK" || response.status === "200") {
- let finalResult = response.imageId;
- this.imageId = finalResult;
- console.log('Successfully uploaded!');
- }
- else {
- this.isVisible = false;
- console.log(response.message);
- }
- },
- onUploadError: function (file, message, xhr) {
- console.log("Message ====> " + JSON.stringify(message));
- }
- }
- });
- });
In this JS file, the “viewImageById” method is used for the read request and “onUploaded”
method is used for the write request. The screen looks like:

Data Access Layer for Data READ & WRITE Operations
I’m guessing you know the EF Core Code First Approach and you have the domain models and context class. You may use a different approach. Here, I’ll implement the Read and Write operations for data access. Look at the diagram below to understand the whole process of the application.

Packages Installation
In “HR.App.DAL.CQRS” project, I’ve installed MediatR.Extensions.Microsoft.DependencyInjection, Microsoft.EntityFrameworkCore and Microsoft.EntityFrameworkCore.SqlServer “ using NuGet Package Manager.

I need the MediatR to implement the Command and Query handlers. I’ll use the MediatR.Extensions for ASP.NET Core to resolve the dependency.
Read Query Handler Implementation
To get an image from the database, I’ve added the GetImageQuery.cs class with the following code:




Join the conversation! Your thoughts help the community grow.