Using QuickChart API in .NET Core MVC.

Photo by Isaac Smith on Unsplash

If you've ever been in a situation that you got asked to provide a simple visualization of data in your application then look no further. QuickChart is an open-source API that uses the famous library Chart.Js. This service is lightweight and easy to use.

Today, I will walk you through how to generate charts using QuickChart.io API.

LET’S GET STARTED!!

1-Creating our Entities and repository.

For the purpose of this demonstration, I will be using VideoGame and Consoles as our Entities where Many videogames relate to one console.
The data will be in memory and we will mock the database repository pattern.

a-Adding the entities

  • VideoGame
public class VideoGame{public int ID { get; set; }public Rating Rating { get; set; }public string Title { get; set; }public Developers Developer { get; set; }public GameConsole Console { get; set; }}
  • GameConsole
public class GameConsole{public GameConsole(){VideoGames = new List<VideoGame>();}public int ID { get; set; }public string ConsoleName { get; set; }public List<VideoGame> VideoGames { get; set; }}
  • Developers Enum
public enum Developers{  Activison,  EA,  Ubisoft}
  • Rating Enum
public enum Rating{  Good,  Bad,  Great}

b-Adding our Respoitories

  • IBaseRepo
public interface IBaseRepo<T>{int Save(T item);int Delete(T item);IQueryable<T> GetIqeryAble();}
  • IVideoGameRepo
public interface IVideoGameRepo : IBaseRepo<VideoGame>{}
  • IGameConsoleRepo
public interface IGameConsoleRepo : IBaseRepo<GameConsole>{}
  • VideoGameRepo
public class VideoGameRepo : IVideoGameRepo{private List<VideoGame> _videoGameList;public VideoGameRepo(){_videoGameList = new List<VideoGame>();Save(new VideoGame{ ID = 0,Rating= Rating.Bad,Title ="Call of duty Bo2",Developer = Developers.Activison });Save(new VideoGame{ ID = 0,Rating= Rating.Great, Title ="Call of duty Bo3", Developer = Developers.Activison });Save(new VideoGame{ ID = 0,Rating= Rating.Good, Title ="Call of duty Bo4", Developer = Developers.Activison });Save(new VideoGame{ ID = 0,Rating= Rating.Good, Title ="Call of duty Bo5", Developer = Developers.Activison });Save(new VideoGame{ ID = 0,Rating= Rating.Great, Title ="Assasins creed 1", Developer = Developers.Ubisoft });Save(new VideoGame{ ID = 0,Rating= Rating.Bad, Title = "Assasins creed 2", Developer = Developers.Ubisoft });Save(new VideoGame{ ID = 0,Rating= Rating.Bad, Title = "Assasins creed 3",Developer = Developers.Ubisoft });Save(new VideoGame{ ID = 0,Rating= Rating.Great, Title = "Assasins creed Brotherhood", Developer = Developers.Ubisoft });Save(new VideoGame{ ID = 0,Rating= Rating.Good,Title ="Battlefield 3", Developer = Developers.EA });Save(new VideoGame{ ID = 0,Rating= Rating.Bad,Title = "Battlefield 4", Developer = Developers.EA });Save(new VideoGame{ ID = 0,Rating= Rating.Good, Title = "Battlefield 5", Developer = Developers.EA });}public int Delete(VideoGame item){var foundGame = _videoGameList.FirstOrDefault(i => i.ID == item.ID);if (foundGame == null){return 0;}else{_videoGameList.Remove(foundGame);return 1;}}public IQueryable<VideoGame> GetIqeryAble(){var queryAble = _videoGameList.AsQueryable();return queryAble;}public int Save(VideoGame item){if(item.ID == 0 && _videoGameList.Count == 0){item.ID = 1;_videoGameList.Add(item);return 1;}else if(item.ID > 0){var foundUpdatedGame = _videoGameList.FirstOrDefault(i => i.ID == item.ID);if (foundUpdatedGame != null){foundUpdatedGame = item;return 1;}else{return 0;}}else{var maxNum = _videoGameList.Max(v => v.ID);item.ID = maxNum + 1;_videoGameList.Add(item);return 1;}}}
  • GameConsoleRepo
public class GameConsoleRepo : IGameConsoleRepo{public List<GameConsole> _gameConsoleList;public GameConsoleRepo(){_gameConsoleList = new List<GameConsole>();Save(new GameConsole{ ID = 0, ConsoleName = "PlayStation 5",VideoGames = new List<VideoGame>{ new VideoGame { ID = 1,Rating= Rating.Bad,Title ="Call of duty Bo2"},new VideoGame {ID = 2,Rating= Rating.Great,Title ="Call of duty Bo3" },new VideoGame {ID = 3,Rating= Rating.Good,Title ="Call of duty Bo4" } }});Save(new GameConsole{ ID = 0, ConsoleName = "PlayStation 4",VideoGames = new List<VideoGame>{ new VideoGame { ID = 4,Rating= Rating.Good,Title ="Call of duty Bo5"},new VideoGame {ID = 5,Rating= Rating.Great,Title ="Assasins creed 1"},new VideoGame {ID = 6,Rating= Rating.Bad,Title = "Assasins creed 2" } }});Save(new GameConsole{ ID = 0, ConsoleName = "Xbox One",VideoGames = new List<VideoGame>{ new VideoGame {ID = 7,Rating= Rating.Bad,Title = "Assasins creed 3"},new VideoGame {ID = 8,Rating= Rating.Great,Title = "Assasins creed Brotherhood"},new VideoGame {ID = 9,Rating= Rating.Good,Title ="Battlefield 3" } }});Save(new GameConsole{ ID = 0, ConsoleName = "Xbox Series X",VideoGames = new List<VideoGame>{ new VideoGame {  ID = 0,Rating= Rating.Bad,Title = "Battlefield 4" },new VideoGame {ID = 0,Rating= Rating.Good,Title = "Battlefield 5" }}});}public int Delete(GameConsole item){var foundGame = _gameConsoleList.FirstOrDefault(i => i.ID == item.ID);if (foundGame == null){return 0;}else{_gameConsoleList.Remove(foundGame);return 1;}}public IQueryable<GameConsole> GetIqeryAble(){var queryAble = _gameConsoleList.AsQueryable();return queryAble;}public int Save(GameConsole item){if (item.ID == 0 && _gameConsoleList.Count == 0){item.ID = 1;_gameConsoleList.Add(item);return 1;}else if (item.ID > 0){var foundUpdatedGame = _gameConsoleList.FirstOrDefault(i => i.ID == item.ID);if (foundUpdatedGame != null){foundUpdatedGame = item;return 1;}else{return 0;}}else{var maxNum = _gameConsoleList.Max(v => v.ID);item.ID = maxNum + 1;_gameConsoleList.Add(item);return 1;}}}
  • I have created a Generic interface that has most of the common methods that VideoGame and GameConsole repos will need and, I have implemented the corresponding interfaces to their respective concrete classes.

2-Adding Charts related models

a-Adding the Chart Types

  • ChartBase
public abstract class ChartBase{public string Type { get; set; }public DataModel Data { get; set; }}
  • DoughnoutChart
public class DoughnutChart : ChartBase{public DoughnutChart(){Type = "doughnut";}public object Options{get{return new{Plugins = new{datalabels = new{display = true,backgroundColor = "#ccc",borderRadius = 10,font = new{color = "red",size = 12}}}};}}}
  • PieChart
public class PieChart : ChartBase{public PieChart(){Type = "pie";}public object Options{get{return new{Plugins = new{datalabels = new{display = true,align = "bottom",backgroundColor = "#ccc",borderRadius = 6,font = new{color = "red",size = 12}}}};}}}

I have created a base class and had other charts derive from it. the Options property is required when we convert the object to JSON string and POST it to the API endpoint.

b-Adding our API specific models

  • DataModel
public class DataModel{  public List<string> Labels { get; set; }  public List<DataSetModel> Datasets { get; set; }}
  • DataSetModel
public class DataSetModel{  public string Label { get; set; }  public List<int> Data { get; set; }}
  • ClientResponseModel
public class ClientResponseModel{  public bool success { get; set; }  public string Url { get; set; }}

C-Adding Generic Client class

  • IBaseClient
public interface IBaseClient<T>{Task<ClientResponseModel> GetChartResult(T item);}
  • BaseClient
public class BaseClient<T> : HttpClient, IBaseClient<T> where T : class{private string BasePath;private const string MEDIA_TYPE = "application/json";public BaseClient(string baseAddress, string basePath){BaseAddress = new Uri(baseAddress);BasePath = basePath;}public async Task<ClientResponseModel> GetChartResult(T item){try{SetupHeaders();var objectToSerialize = GetChartObject(item);var serializedJson = GetSerializedObject(objectToSerialize);var bodyContent = GetBodyContent(serializedJson);var response = await PostAsync(BasePath, bodyContent);if (response.IsSuccessStatusCode){var responseString = await response.Content.ReadAsStringAsync();var responseModel = JsonConvert.DeserializeObject<ClientResponseModel>(responseString);return responseModel;}else{throw new Exception(response.StatusCode.ToString());}}catch (Exception ex){throw new Exception($"Something went wrong {ex.Message}");}}#region Request Helpersprotected virtual void SetupHeaders(){DefaultRequestHeaders.Clear();//Define request data formatDefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MEDIA_TYPE));}protected override void Dispose(bool disposing){base.Dispose(disposing);}protected virtual string GetSerializedObject(object obj){var serializerSettings = new JsonSerializerSettings();serializerSettings.ContractResolver =new CamelCasePropertyNamesContractResolver();var serializedJson = JsonConvert.SerializeObject(obj, serializerSettings);return serializedJson;}protected virtual StringContent GetBodyContent(string serializedJson){var bodyContent = new StringContent(serializedJson, Encoding.UTF8, "application/json");return bodyContent;}private object GetChartObject(object item, int width = 400, int height = 250){var objToSerialize = new{Width = width,Height = height,Chart = item,};return objToSerialize;}#endregion}
  • Adding JSON properties to appsettings.json
  • The client class will establish the connection to the API endpoint and retrieve the corresponding response from the server.

3-Creating the ChartService.

  • IChartService
public interface IChartService{//Chart methodsChartBase GetVideoGameRatings();ChartBase GetVideoGameDeveloper();ChartBase GetConsoleGames();//Chart generation methodsTask<ClientResponseModel> GetVideoGameRatingChart();Task<ClientResponseModel> GetVideoGameDeveloperChart();Task<ClientResponseModel> GetConsoleGamesChart();//Chart GeneratorChartBase GetChartInstance(string type, List<string> labels, List<int> data);}
  • ChartService
public class ChartService : IChartService{private readonly IBaseClient<ChartBase> _client;private readonly IVideoGameRepo _gameRepo;private readonly IGameConsoleRepo _consoleRepo;private readonly ChartFactory _chartFactory;public ChartService(IBaseClient<ChartBase> client,IVideoGameRepo gameRepo,IGameConsoleRepo consoleRepo,ChartFactory chartFactory){_client = client;_gameRepo = gameRepo;_consoleRepo = consoleRepo;_chartFactory = chartFactory;}#region Charts Methodspublic ChartBase GetVideoGameDeveloper(){var gamesList = _gameRepo.GetIqeryAble();var developerGroup = gamesList.GroupBy(i => i.Developer).OrderBy(i => i.Key);var developerList = developerGroup.Select(m => m.Count()).ToList();var chart = GetChartInstance("Pie",new List<string>(Enum.GetNames(typeof(Developers)).ToList()),new List<int>(developerList));return chart;}public ChartBase GetVideoGameRatings(){var gamesList = _gameRepo.GetIqeryAble();var ratingsGroup = gamesList.GroupBy(i => i.Rating).OrderBy(i => i.Key);var ratingsList = ratingsGroup.Select(m => m.Count()).ToList();var chart = GetChartInstance("Pie",new List<string>(Enum.GetNames(typeof(Rating)).ToList()),new List<int>(ratingsList));return chart;}public ChartBase GetConsoleGames(){var consoleList = _consoleRepo.GetIqeryAble();consoleList = consoleList.OrderBy(i => i.ID);var consoleName = consoleList.Select(c => c.ConsoleName);var consoleGames = consoleList.Select(c => c.VideoGames.Count);var chart = GetChartInstance("Doughnut", new List<string>(consoleName),new List<int>(consoleGames));return chart;}#endregion#region Charts Cinversionspublic async Task<ClientResponseModel> GetConsoleGamesChart(){var consoleGamesChart = GetConsoleGames();var response = await _client.GetChartResult(consoleGamesChart);return response;}public async Task<ClientResponseModel> GetVideoGameDeveloperChart(){var gameDevelopers = GetVideoGameDeveloper();var response = await _client.GetChartResult(gameDevelopers);return response;}public async Task<ClientResponseModel> GetVideoGameRatingChart(){var videoGamesRating = GetVideoGameRatings();var response = await _client.GetChartResult(videoGamesRating);return response;}#endregionpublic ChartBase GetChartInstance(string type, List<string> labels, List<int> data){var chartInstance = _chartFactory.GetChartInstance(type);chartInstance.Data = new DataModel{Labels = new List<string>(labels),Datasets = new List<DataSetModel>{ new DataSetModel{Data = new List<int>(data)}}};return chartInstance;}}
  • ChartFactory
public class ChartFactory{public ChartBase GetChartInstance(string userSelection){if (userSelection == "Pie")return new PieChart();return new DoughnutChart();}}
  • Creating a ViewModel for presentation
    ChartViewModel
public class ChartViewModel{public ChartViewModel(){Charts = new List<ClientResponseModel>();}public List<ClientResponseModel> Charts { get; set; }}

4- Specifying the Dependencies

  • Startup.cs
public void ConfigureServices(IServiceCollection services){var chartClientBaseAddress = Configuration.GetSection("chartClientBaseAddress").Value;var chartClientBasePath = Configuration.GetSection("chartClientBasePath").Value;services.AddScoped<IBaseClient<ChartBase>>(c => new BaseClient<ChartBase>(chartClientBaseAddress, chartClientBasePath));services.AddScoped<IChartService, ChartService.Helper.ChartService>();services.AddScoped<ChartViewModel>();services.AddSingleton<IGameConsoleRepo, GameConsoleRepo>();services.AddSingleton<IVideoGameRepo, VideoGameRepo>();services.AddSingleton<ChartFactory>();services.AddControllersWithViews();}

5-Wiring things up.

  • HomeController
public class HomeController : Controller{private readonly ILogger<HomeController> _logger;private readonly IChartService _chartService;public HomeController(ILogger<HomeController> logger,IChartService chartService){_logger = logger;_chartService = chartService;}public async Task<IActionResult> Index(){var consoleGamesChart =  _chartService.GetConsoleGamesChart();var videoGameDeveloperChart = _chartService.GetVideoGameDeveloperChart();var videoGameRatingChart = _chartService.GetVideoGameRatingChart();await Task.WhenAll(consoleGamesChart,videoGameDeveloperChart, videoGameRatingChart);var viewModel = new ChartViewModel{Charts = new List<ClientResponseModel> { consoleGamesChart.Result,videoGameDeveloperChart.Result, videoGameRatingChart.Result  }};return View(viewModel);}public IActionResult Privacy(){return View();}[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]public IActionResult Error(){return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });}}
  • Index.cshtml
@model ChartViewModel@{ViewData["Title"] = "Home Page";}<div class="text-center"><h1 class="display-4 text-info">Generate Chart Images using QuickChart API</h1><p class="text-dark display-4">Samples Generated</p><div class="row">@foreach (var image in Model.Charts){<div class="col-4"><div class="card"><img class="card-img-top" src="@image.Url" /></div></div>}</div></div>
  • Final Result

Conclusion

Photo by Joshua Sortino on Unsplash

In this demonstration I haven’t expanded on all the chart types there are in QuickChart. If you check the solution on Github(link). the project is built in a way you can expand more on it and apply different charts to the data you'd like to represent.

Quick Chart Docs
Git Hub Repo
LinkedIn

See you at the next one!!

--

--

--

Software developer | Programming and Blockchain enthusiast

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Eight factors to build more secure and performance software

“Testing Bits: 382 — Feb 28th — March 6th, 2021

Hack The Box: ScriptKiddie Writeup

atomic nonatomic retain assign copy

How Neptune Finance Can Help Solve Anchor’s Problem

Xpansion Game Weekly Update

Installing EOS (Part 1) — Setting up Ubuntu on VirtualBox

Onboarding SWE? Questions to ask in your first week

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Ibrahim Jaber

Ibrahim Jaber

Software developer | Programming and Blockchain enthusiast

More from Medium

Deploy an Angular application with Azure app service

Publish a .NET Console App to Chocolatey using GitHub Actions

Create a web API using minimal API ASP.NET Core

Setup visual studio 2022 and Web application with ASP.NET MVC 6.0