jsonasp.net-mvcrestasp.net-mvc-5jsonplaceholder

Handle Related Records with JSONPlaceholder and MVC 5


I'm trying to display a paged table of "Albums" data in an MVC 5 application, with the data values coming from different records using the JSONPlaceholder REST API:

https://jsonplaceholder.typicode.com/

Users: (10 records)

{
    "id": 1,
    "name": "Leanne Graham",
    "username": "Bret",
    "email": "Sincere@april.biz",
    "address": {
      "street": "Kulas Light",
      "suite": "Apt. 556",
      "city": "Gwenborough",
      "zipcode": "92998-3874",
      "geo": {
        "lat": "-37.3159",
        "lng": "81.1496"
      }
    },
    "phone": "1-770-736-8031 x56442",
    "website": "hildegard.org",
    "company": {
      "name": "Romaguera-Crona",
      "catchPhrase": "Multi-layered client-server neural-net",
      "bs": "harness real-time e-markets"
    }
}

Albums: (100 records)

{
    "userId": 1,
    "id": 1,
    "title": "quidem molestiae enim"
}

Photos: (5000 records)

{
    "albumId": 1,
    "id": 1,
    "title": "accusamus beatae ad facilis cum similique qui sunt",
    "url": "https://via.placeholder.com/600/92c952",
    "thumbnailUrl": "https://via.placeholder.com/150/92c952"
}

The table will show fields from /users, /albums, and /photos. Some of the values are nested such as the address values for a user. I created the individual model classes using https://json2csharp.com/. Next I plan to create the AlbumsViewModel with the fields I need and IEnumerable properties for UserModel, AlbumModel and PhotoModel classes. I'm unsure of how to fetch the data from these different resources and how to represent the cardinality between these entities in the AlbumsViewModel to present a custom view which would display the following:

Thumbnail | Title | Name | Email | Phone | Address

View Model:

public class AlbumViewModel
{
    public string ThumbnailUrl { get; set; }

    public string Title { get; set; }

    public string Name { get; set; }

    public string Email { get; set; }

    public string Phone { get; set; }

    public string Address { get; set; }
}

UPDATE

I've got the records displaying but have two remaining issues:

1) Is there a more performant way of doing this than using three nested loops?

2) How to implement paging in MVC 5? I've seen the PagedList Nuget package (no longer maintained). Is this the best available option?

UPDATE 2

I have the paging implemented using the PagedList.MVC package. The only remaining problem is with the AlbumViewModel fields being populated from the three different resources. I originally thought it was just a performance issue but it looks like I'm doing it wrong. I'm getting 5000 records back; is this correct? Can anyone suggest a better, correct way of doing this? Maybe with LINQ?

public static class AlbumsModelBAL
{
    private const string albumsAddress =
       "http://jsonplaceholder.typicode.com/albums";
    private const string usersAddress =
        "http://jsonplaceholder.typicode.com/users"
    private const string photosAddress =
        "http://jsonplaceholder.typicode.com/photos";
    
    public static List<AlbumViewModel> GetAlbums()
    {
        List<User> users = null;
        List<AlbumModel> albums = null;
        List<PhotoModel> photos = null;
    
        AlbumViewModel albumVM = null;
        List<AlbumViewModel> albumsList = new List<AlbumViewModel>();
    
        string thumbnailURL, title, name, email, phone, address = string.Empty;
            
    
        using (var client = new HttpClient())
        {
            var usersResponse = client.GetAsync(usersAddress).Result;
            var albumsResponse = client.GetAsync(albumsAddress).Result;
            var photosResponse = client.GetAsync(photosAddress).Result;
    
            if (usersResponse.IsSuccessStatusCode && albumsResponse.IsSuccessStatusCode && photosResponse.IsSuccessStatusCode)
            {
                var usersResponseContent = usersResponse.Content;
                string usersResponseString = usersResponseContent.ReadAsStringAsync().Result;
                users = JsonConvert.DeserializeObject<List<User>>(usersResponseString);
    
                var albumsResponseContent = albumsResponse.Content;
                string albumsResponseString = albumsResponseContent.ReadAsStringAsync().Result;
                albums = JsonConvert.DeserializeObject<List<AlbumModel>>(albumsResponseString);
    
                var photosResponseContent = photosResponse.Content;
                string photosResponseString = photosResponseContent.ReadAsStringAsync().Result;
                photos = JsonConvert.DeserializeObject<List<PhotoModel>>(photosResponseString);
    
                // This is the problem
                for (int i = 0; i < users.Count; i++)
                {
                    for (int j = 0; j < albums.Count; j++)
                    {
                        if (users[i].Id == albums[j].UserId)
                            for (int k = 0; k < photos.Count; k++)
                            {
                                if (albums[j].Id == photos[k].AlbumId)
                                {
                                    thumbnailURL = photos[k].ThumbnailUrl;
                                    title = albums[j].Title;
                                    name = users[i].Name;
                                    email = users[i].Email;
                                    phone = users[i].Phone;
                                    address = users[i].Address.Street + ", " 
                                        + users[i].Address.Suite + ", " 
                                        + users[i].Address.City + ", " 
                                        + users[i].Address.Zipcode;
    
                                    albumVM = new AlbumViewModel
                                    {
                                        ThumbnailUrl = thumbnailURL,
                                        Title = title,
                                        Name = name,
                                        Email = email,
                                        Phone = phone,
                                        Address = address
                                    };
                                    albumsList.Add(albumVM);
                                }
                            }
                    }
                }
            }
        }
    
        return albumsList;
    }

enter image description here


Solution

  • Linq join at rescue.

    Let's suppose this classes close to yours:

    public class User
    {
        public int UserId {set; get;}
        public string Username {get; set;} 
    }
    public class Album
    {
        public int UserId {set; get;}
        public int AlbumId {set; get;}
        public string Albumname {get; set;} 
    }
    public class Photo
    {
        public int AlbumId {set; get;}
        public int PhotoId {set; get;}
        public string Photoname {get; set;}
    }
    public class AlbumVM 
    {
        public string Username {get; set;} 
        public string Albumname {get; set;}
        public string Photoname {get; set;}
    }
    

    This will be the Linq Expression to get your results:

    
    using System.Linq;
    // ...
    
    var joinAll = 
        users
        .Join(
            albumns,  // joining users with albums with this 2 keys:
            user=>user.UserId,
            album=>album.UserId,
            (user, album) => new 
                {
                    User = user,
                    Album = album
                }
        )
        .Join(
            photos,  // joining photos with previous join with this 2 keys:
            useralbum => useralbum.Album.AlbumId,
            photo => photo.AlbumId,
            (useralbum, photo) => new AlbumVM   // generating new AlbumVM
                {
                    Albumname = useralbum.Album.Albumname,
                    Username = useralbum.User.Username,
                    Photoname = photo.Photoname
                }
        );
    

    Be free to copy-paste the linq and adapt it to your classes and properties.

    More info:

    Full code: https://gist.github.com/ctrl-alt-d/89064f253f187e255ead7421d8162dd1