Sunday, 3 January 2016

Cloning Reddit to learn ASP.NET 5 - Part 5

In the last post we finally started to do something to the front end, namely the SubReddit bar on top, but the values were hard coded and they weren't even links.

Let's try to remedy that.

There are a few things we need to do:
  • How do we identify Default SubReddits?
  • How do we get and display said SubReddits?
The first one is easy, we add a new property to our SubReddit class, which now looks like this:
using System.Collections.Generic;

namespace Reddit.Models
{
    public class SubReddit
    {
        public int SubRedditId { get; set; }
        public string Name { get; set; }
        public bool IsPublic { get; set; }
        public List<Post> Posts { get; set; }
        public List<RedditUser> Users { get; set; }
        public bool IsBanned { get; set; }
        public bool IsDefault { get; set; }
    }
}

There are other ways of doing this, say, have a default SubReddit table an add the default SubReddits there, but we'll do it this way until I change my mind.

We need to run now through the Migrations rigmarole again, so from ..\src\Reddit run the following commands:
dnx ef Migrations add isDefaultSubReddit
dnx ef database update
I've manually seeded the SubReddit table with a few records, both default and not default (I will discuss how to seed it from code in a later post) and now we can edit redditApp.js to this:
(function () {

  "use strict";

  var redditApp = angular.module('redditApp', []);

redditApp.controller('SubRedditListCtrl', ['$scope', '$http', function($scope, $http) {
  $http.get("/api/SubReddit").then(function(data){
  $scope.subReddits =data.data ;
})}
])
  
})();

In order to get around issues with minification, we inject the dependencies as strings which will not be minified.

There are several issues with this approach as we're getting more data than we need in two senses:

  • We're getting all SubReddits, we could filter them but that would mean retrieving all the data from the server and then filtering them, which is as inefficient as it sounds. 
  • Furthermore, we're retrieving all properties of the SubReddit objects when we only need the name.
Let's deal with the first of these issues. We add a new method called GetDefaultSubReddits to the Repository, make sure you add it to the Interface as well:

        public IEnumerable<SubReddit> GetDefaultReddits()
        {
            return ctx.SubReddits.Where(x => x.IsDefault && x.IsPublic && !x.IsBanned);
        }
And we also add a call to this method from the Controller:
.        [HttpGet("[action]")]
        public IEnumerable<SubReddit> GetDefault()
        {
            return repository.GetDefaultSubReddits();
        }
Note that I'm using an action here, which is effectively the name of the method
Now we modify the redditApp.js
(function () {

  "use strict";

  var redditApp = angular.module('redditApp', []);

redditApp.controller('SubRedditListCtrl', ['$scope', '$http', function($scope, $http) {
  $http.get("/api/SubReddit/GetDefault").then(function(data){
  $scope.subReddits =data.data ;
})}
])
  
})();
At this point we've solved issue number one but we're still sending quite a bit of unnecessary data up and down the wire so let's build a DTO that contains just the data that we need, which in this case is simply the Name. There are other ways of achieving this, for instance we could use an OData query to simply retrieve the necessary data.

This is the SubRedditDTO

namespace Reddit.Models
{
    public class SubRedditDTO
    {
        public string Name { get; set; }
    }
}
Modified GetDefault Method, I'm not sure whether it's a better idea to transform to DTO object here or on the Repository. I guess it depends on usage, in other words, if the Repository method will be used somewhere else it makes sense to do the transformation here.
        [HttpGet("[action]")]
        public IEnumerable<SubRedditDTO> GetDefault()
        {
            return repository.GetDefaultSubReddits().Select(x => new SubRedditDTO() { Name = x.Name });
        }
This is the reponse from the server now
[{"Name":"Gadgets"},{"Name":"Sports"},{"Name":"Gaming"},{"Name":"Pics"},{"Name":"WorldNews"},{"Name":"Videos"},{"Name":"AskReddit"},{"Name":"Aww"}]
This is before, when we where not using a DTO.
[{"SubRedditId":3,"Name":"Gadgets","IsPublic":true,"Posts":null,"Users":null,"IsBanned":false,"IsDefault":true},{"SubRedditId":4,"Name":"Sports","IsPublic":true,"Posts":null,"Users":null,"IsBanned":false,"IsDefault":true},{"SubRedditId":5,"Name":"Gaming","IsPublic":true,"Posts":null,"Users":null,"IsBanned":false,"IsDefault":true},{"SubRedditId":6,"Name":"Pics","IsPublic":true,"Posts":null,"Users":null,"IsBanned":false,"IsDefault":true},{"SubRedditId":7,"Name":"WorldNews","IsPublic":true,"Posts":null,"Users":null,"IsBanned":false,"IsDefault":true},{"SubRedditId":8,"Name":"Videos","IsPublic":true,"Posts":null,"Users":null,"IsBanned":false,"IsDefault":true},{"SubRedditId":9,"Name":"AskReddit","IsPublic":true,"Posts":null,"Users":null,"IsBanned":false,"IsDefault":true},{"SubRedditId":10,"Name":"Aww","IsPublic":true,"Posts":null,"Users":null,"IsBanned":false,"IsDefault":true}]
This might seem like a paltry gain but little savings like this can quickly add up.

No comments:

Post a Comment